Conversation
# Conflicts: # apps/self-hosted/package.json # apps/self-hosted/src/core/i18n.ts
📝 WalkthroughWalkthroughAdds a new tipping feature to the self-hosted app: configuration schema, types, tip execution utilities, UI components (button, two-step popover, currency cards, QR), i18n keys, build/dependency updates, and integration points in blog sidebar and post footer. Changes
Sequence DiagramsequenceDiagram
actor User
participant TipButton
participant TippingPopover
participant TippingStepAmount
participant TippingStepCurrency
participant executeTip
participant broadcast
participant Blockchain
User->>TipButton: click
TipButton->>TippingPopover: open (positioned via floating-ui)
TippingPopover->>TippingStepAmount: show amount UI
User->>TippingStepAmount: select/enter amount
TippingStepAmount-->>TippingPopover: amount
TippingPopover->>TippingStepCurrency: show currency UI
User->>TippingStepCurrency: select asset (or external wallet)
TippingStepCurrency-->>TippingPopover: asset, memo
TippingPopover->>executeTip: executeTip(from,to,amount,asset,memo)
executeTip->>executeTip: fetch asset price / compute real amount
executeTip->>broadcast: build ops and broadcast(authorityType)
broadcast->>Blockchain: submit tx
Blockchain-->>broadcast: confirmation
broadcast-->>executeTip: success
executeTip-->>TippingPopover: success -> close
TippingPopover-->>User: confirmation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. 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: 15
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/self-hosted/package.json (1)
14-65:⚠️ Potential issue | 🟠 MajorAlign dependency versions with web app and remove extraneous packages
Three dependencies have version mismatches with the web app that break architectural parity:
@floating-ui/dom: self-hosted has^1.7.4, web app has^1.6.13@floating-ui/react-dom: self-hosted has^2.1.6, web app has^2.1.2qrcode: self-hosted has^1.5.4, web app has^1.5.3Additionally,
@rsbuild/plugin-node-polyfillis present in self-hosted but absent from the web app, and should be removed unless there is a specific architectural reason for it.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/self-hosted/package.json` around lines 14 - 65, Update the dependency versions in package.json to match the web app: change "@floating-ui/dom" to "^1.6.13", "@floating-ui/react-dom" to "^2.1.2", and "qrcode" to "^1.5.3"; also remove the devDependency "@rsbuild/plugin-node-polyfill" unless you have a documented reason to keep it so the self-hosted app remains architecturally aligned with the web app.apps/self-hosted/src/features/auth/auth-actions.ts (1)
119-133:⚠️ Potential issue | 🟠 Major
authorityTypesilently ignored for HiveAuth and Hivesigner — active-key tips will fail with opaque errors.Tipping with HIVE or HBD requires active authority (
ASSETS_REQUIRING_KEYintypes.ts). Whenbroadcast()is called withauthorityType: "Active"and the user is logged in viahiveauthorhivesigner, the authority type is silently dropped. The downstream blockchain error ("missing required active authority") will surface as a raw chain error rather than a user-friendly message.Consider throwing an early, actionable error for these auth methods when an active-key operation is attempted:
🛡️ Proposed guard
const authorityType = options?.authorityType ?? "Posting"; switch (user.loginType) { case "keychain": return keychainBroadcast(user.username, operations, authorityType); case "hivesigner": if (!user.accessToken) { throw new Error("No access token available"); } + if (authorityType === "Active") { + // Hivesigner token scope was set at login; active-key ops succeed only + // if the token was granted active scope. No early validation is possible here, + // but callers should be aware of this limitation. + } return broadcastWithHivesigner(user.accessToken, operations); case "hiveauth": if (!session) { throw new Error("No HiveAuth session available"); } + if (authorityType === "Active") { + throw new Error( + "HiveAuth sessions use posting authority. Switch to Keychain for transfers." + ); + } return broadcastWithHiveAuth(session, operations);At a minimum, document the limitation for each non-keychain path so callers can gate the tipping UI accordingly.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/self-hosted/src/features/auth/auth-actions.ts` around lines 119 - 133, The switch in broadcast (auth-actions.ts) drops authorityType for non-keychain logins; update the branches that call broadcastWithHivesigner and broadcastWithHiveAuth to validate authorityType and throw a clear, actionable error when authorityType === "Active" (or ASSETS_REQUIRING_KEY) instead of proceeding (so callers get a user-friendly message rather than a raw chain error). Specifically, inside the "hivesigner" branch (before calling broadcastWithHivesigner) and the "hiveauth" branch (before calling broadcastWithHiveAuth) check the passed authorityType/session and throw a descriptive Error like "Active key required for this operation with hivesigner/hiveauth" if an active-level operation is attempted; also add a short comment documenting the limitation so callers can gate tipping UI accordingly.
🧹 Nitpick comments (9)
apps/self-hosted/package.json (1)
14-15: Use@floating-ui/reactfor cleaner interaction handling instead of manual state managementThe current implementation manually handles interactions (click-to-open via
useStateand outside-click dismiss viamousedownevent listeners). While this approach works,@floating-ui/reactprovides dedicated interaction hooks (useClick,useDismiss,useInteractions) that would simplify this logic and reduce boilerplate intip-button.tsxandtipping-popover.tsx.Consider replacing
@floating-ui/react-domwith@floating-ui/reactto leverage these built-in interaction primitives.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/self-hosted/package.json` around lines 14 - 15, Replace the manual open-state and mousedown outside-click logic in tip-button.tsx and tipping-popover.tsx with Floating UI's interaction hooks: change the dependency from "@floating-ui/react-dom" to "@floating-ui/react" in package.json, import useFloating plus useClick, useDismiss and useInteractions from '@floating-ui/react', and wire them into the components (useClick to toggle open, useDismiss to handle outside clicks/escape, useInteractions to combine handlers). Remove the manual useState-based toggle and the document 'mousedown' listener/cleanup in tip-button.tsx and ensure the floating context/props from useFloating are passed through to tipping-popover.tsx so the built-in dismissal and click behavior replace the existing custom logic.apps/self-hosted/rsbuild.config.ts (2)
20-22: Duplicate bip39 alias entries across both resolution layers.
bip39andbip39-originalare defined identically in bothresolve.alias(rsbuild-level, lines 21–22) andtools.rspack.resolve.alias(rspack-level, lines 34–35). rsbuild mergesresolve.aliasinto the underlying rspack config, so the entries intools.rspack.resolve.aliasare redundant. The existing React aliases were only in the rspack layer, so this introduces an inconsistent pattern. Consider placing bip39 aliases in one layer only, consistent with where the React aliases live.♻️ Proposed cleanup
resolve: { alias: { '@': './src', - // bip39 has only named exports; wallets uses default import — shim provides default - 'bip39': path.resolve(__dirname, 'src/shim-bip39.ts'), - 'bip39-original': bip39Resolved, }, }, tools: { rspack: { resolve: { alias: { react: path.resolve(__dirname, 'node_modules/react'), 'react-dom': path.resolve(__dirname, 'node_modules/react-dom'), '@ecency/ui': path.resolve(__dirname, '../../packages/ui/dist/index.js'), + // bip39 has only named exports; wallets uses default import — shim provides default 'bip39': path.resolve(__dirname, 'src/shim-bip39.ts'), 'bip39-original': bip39Resolved, }, },Also applies to: 34-35
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/self-hosted/rsbuild.config.ts` around lines 20 - 22, The bip39 aliases are duplicated between resolve.alias and tools.rspack.resolve.alias (the keys 'bip39' and 'bip39-original'); remove the redundant pair from one layer so aliases live only in the same layer as the React aliases for consistency—either keep them in resolve.alias (rsbuild-level) and delete them from tools.rspack.resolve.alias, or vice versa; update only the entries named 'bip39' and 'bip39-original' and ensure the shim path (src/shim-bip39.ts) and bip39Resolved symbol remain referenced in the surviving alias.
16-16: Consider scopingpluginNodePolyfillto only the modules bip39 actually needs.The plugin supports an
includeoption to specify which modules should be polyfilled; if set, only those modules are injected. Without it, the plugin polyfills a large set of built-ins:assert,buffer,crypto,http,https,stream,os,path, and many more.bip39only requirescryptoandbuffer, so the current unconstrained call inflates the bundle with unnecessary polyfills.♻️ Proposed scoping
- plugins: [pluginReact(), pluginNodePolyfill()], + plugins: [pluginReact(), pluginNodePolyfill({ include: ['buffer', 'crypto'] })],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/self-hosted/rsbuild.config.ts` at line 16, The pluginNodePolyfill invocation in the plugins array is currently unscoped and injects many unnecessary polyfills; update the plugins entry that references pluginNodePolyfill (in the plugins array) to pass an include option limited to only the built-ins bip39 needs (e.g., "crypto" and "buffer") so only those modules are polyfilled and the bundle size is reduced.apps/self-hosted/src/shim-bip39.ts (1)
8-13: Considerexport * from 'bip39-original'for maintainability.The shim currently re-exports 6 of bip39's 9 public APIs; it omits
wordlists,setDefaultWordlist, andgetDefaultWordlist. While these are not imported anywhere in the current codebase, usingexport * from 'bip39-original'would automatically forward the full API surface and reduce maintenance burden if usage patterns expand in the future.🔄 Suggested refactor
import * as bip39 from 'bip39-original'; export default bip39; -export const generateMnemonic = bip39.generateMnemonic; -export const mnemonicToSeedSync = bip39.mnemonicToSeedSync; -export const mnemonicToSeed = bip39.mnemonicToSeed; -export const entropyToMnemonic = bip39.entropyToMnemonic; -export const mnemonicToEntropy = bip39.mnemonicToEntropy; -export const validateMnemonic = bip39.validateMnemonic; +export * from 'bip39-original';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/self-hosted/src/shim-bip39.ts` around lines 8 - 13, Replace the five/fewer explicit re-exports (generateMnemonic, mnemonicToSeedSync, mnemonicToSeed, entropyToMnemonic, mnemonicToEntropy, validateMnemonic) with a single module re-export so the shim forwards the entire bip39-original public API (including wordlists, setDefaultWordlist, getDefaultWordlist); update the export statement to use a wildcard export from 'bip39-original' so future additions to that module are automatically exposed without changing this file.apps/self-hosted/src/features/tipping/types.ts (1)
3-9:TippingAssetandTIPABLE_ASSETScan silently diverge — derive one from the other.Both the union type and the runtime array list the same three values. Adding a fourth asset to the type without updating the array (or vice-versa) will cause
isTipableAssetto behave incorrectly at runtime with no compile-time error.♻️ Proposed refactor — single source of truth
-export type TippingAsset = "HIVE" | "HBD" | "POINTS"; - -const TIPABLE_ASSETS: TippingAsset[] = ["HIVE", "HBD", "POINTS"]; +const TIPABLE_ASSETS = ["HIVE", "HBD", "POINTS"] as const; +export type TippingAsset = (typeof TIPABLE_ASSETS)[number];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/self-hosted/src/features/tipping/types.ts` around lines 3 - 9, Replace the duplicated runtime and type definitions by making TIPABLE_ASSETS the single source of truth (declare const TIPABLE_ASSETS = ["HIVE","HBD","POINTS"] as const) and derive the TippingAsset union from it (type TippingAsset = typeof TIPABLE_ASSETS[number]); then update isTipableAsset to use TIPABLE_ASSETS.includes(asset as TippingAsset) exactly as before (keeping the type predicate signature isTipableAsset(asset: string): asset is TippingAsset) so compile-time and runtime values cannot diverge; ensure you remove the old explicit union declaration and export the derived types/names unchanged.apps/self-hosted/src/features/tipping/components/tipping-step-amount.tsx (1)
21-30: Duplicate values inpresetAmountsproduce duplicate React keys and identical buttons.If an operator configures
[1, 1, 5], React will warn about duplicate keys and two$1buttons appear. A simple deduplicate or a runtime guard would prevent this:🛡️ Defensive deduplicate
-{presetAmounts.map((val) => ( +{[...new Set(presetAmounts)].map((val) => (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/self-hosted/src/features/tipping/components/tipping-step-amount.tsx` around lines 21 - 30, presetAmounts can contain duplicates which leads to duplicate React keys and identical buttons; before rendering in tipping-step-amount.tsx, produce a deduplicated list (e.g., derive uniqueAmounts from presetAmounts using a Set or filter by indexOf) and map over uniqueAmounts when creating the <button> elements (referencing presetAmounts, uniqueAmounts, onSelect and the button key) so each rendered button has a unique key and duplicate values are not shown.apps/self-hosted/src/features/tipping/components/tip-button.tsx (1)
3-4: Consolidate Floating UI imports to a single package.
autoUpdateandoffsetare imported from@floating-ui/dom, whileflip,shift, anduseFloatingcome from@floating-ui/react-dom. The official Floating UI docs show importingautoUpdatefrom@floating-ui/react(or@floating-ui/react-dom) when using it withuseFloating'swhileElementsMountedoption.offsetis also importable directly from@floating-ui/react-dom.♻️ Proposed consolidation
-import { autoUpdate, offset } from "@floating-ui/dom"; -import { flip, shift, useFloating } from "@floating-ui/react-dom"; +import { autoUpdate, flip, offset, shift, useFloating } from "@floating-ui/react-dom";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/self-hosted/src/features/tipping/components/tip-button.tsx` around lines 3 - 4, Consolidate the Floating UI imports by replacing the split imports with a single import from "@floating-ui/react-dom" that includes autoUpdate, offset, flip, shift, and useFloating so that the whileElementsMounted option receives the correct autoUpdate implementation; update the import statement to import all those symbols (autoUpdate, offset, flip, shift, useFloating) from "@floating-ui/react-dom".apps/self-hosted/src/features/tipping/hooks/use-tipping-config.ts (1)
9-22:InstanceConfigManager.getConfig()result is absent fromuseMemodeps — config changes won't trigger recomputation.Since
InstanceConfigManager.getConfig()is called inside the memo but isn't listed as a dependency, any runtime config update won't cause the hook to recompute. IfInstanceConfigManageris truly a static, load-once store this is intentional. If it can update at runtime (e.g., hot-reload or dynamic config), the stale value would silently persist untilvariantchanges.Additionally,
DEFAULT_PRESETS(and potentiallytipping.amounts) is returned by reference. If a consumer mutates the returnedpresetAmounts, the shared constant would be affected. Consider returning a copy:♻️ Proposed defensive copy
- const presetAmounts = Array.isArray(tipping.amounts) && tipping.amounts.length > 0 - ? tipping.amounts - : DEFAULT_PRESETS; + const presetAmounts = Array.isArray(tipping.amounts) && tipping.amounts.length > 0 + ? [...tipping.amounts] + : [...DEFAULT_PRESETS];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/self-hosted/src/features/tipping/hooks/use-tipping-config.ts` around lines 9 - 22, The hook calls InstanceConfigManager.getConfig() inside useMemo but doesn’t include the config in the dependency array and also returns preset arrays by reference; fix by reading and including the live config value (result of InstanceConfigManager.getConfig()) in the useMemo deps (or compute config before useMemo and include that variable in deps) so changes retrigger recomputation, and ensure returned presetAmounts and DEFAULT_PRESETS are defensive copies (e.g., copy tipping.amounts or DEFAULT_PRESETS before returning) to avoid exposing shared mutable arrays; update references to InstanceConfigManager.getConfig, useMemo, DEFAULT_PRESETS, tipping.amounts and variant accordingly.apps/self-hosted/src/features/tipping/components/tipping-popover.tsx (1)
99-106: Redundant unsupported-asset check inhandleSubmit.By the time execution reaches line 99,
canSubmit(which requires!isExternalAsset) has already passed. The only scenario this block could fire is if an arbitrary non-TippingAssetstring is stored inselectedAsset— which is only possible because of thestring | undefinedtype. OnceselectedAssetis narrowed toTippingAsset | undefined(see comment above), this guard becomes dead code and can be removed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/self-hosted/src/features/tipping/components/tipping-popover.tsx` around lines 99 - 106, Remove the redundant asset-check in handleSubmit: since canSubmit already ensures !isExternalAsset and selectedAsset should be narrowed to TippingAsset | undefined, delete the block that checks selectedAsset against "HIVE"/"HBD"/"POINTS" and the setError("This asset is not supported for tipping yet") return; instead rely on the existing canSubmit/isExternalAsset narrowing (and fix the selectedAsset type to TippingAsset | undefined where noted) so handleSubmit no longer contains dead-code guarding unsupported assets.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/self-hosted/src/core/configuration-loader.ts`:
- Around line 71-76: The top-level optional property tipping.enabled is an
orphan and should be removed: delete the enabled?: boolean from the tipping type
in configuration-loader.ts so the shape only contains general, post and amounts
(with general.enabled and post.enabled as the feature toggles); update the
corresponding schema/template (config.template.json) and the config-fields.ts to
remove references to tipping.enabled so runtime validation and UI fields align
with the type.
In `@apps/self-hosted/src/features/tipping/components/tipping-currency-card.tsx`:
- Around line 18-20: The useQuery call that passes
getAccountWalletAssetInfoQueryOptions(username, asset) is firing when username
may be an empty string; update the useQuery invocation in tipping-currency-card
(where getAccountWalletAssetInfoQueryOptions is used) to include an enabled:
!!username option so the query only runs when useInstanceConfig().username is
truthy; ensure you keep the same query options object shape and only add the
enabled guard to prevent API calls with an invalid/empty username.
- Around line 22-34: Remove the redundant key prop on the button (it's a no-op
here because the parent TippingCurrencyCards already assigns key={asset}) and
update the button label to fall back to the raw asset symbol when data?.title is
undefined; specifically, change the button rendering in the component that uses
selectedAsset, onAssetSelect and data?.title to display data?.title || asset so
the button isn't blank while the query is loading.
In `@apps/self-hosted/src/features/tipping/components/tipping-currency-cards.tsx`:
- Around line 19-27: The query for walletList fires even when username is empty;
update the useQuery call that uses getAccountWalletListQueryOptions(username,
"usd") so it includes an enabled: !!username guard (same pattern used in
BlogSidebarContent and TippingCurrencyCard) to prevent running the request with
an invalid identifier; modify the options passed into useQuery (or merge into
getAccountWalletListQueryOptions result) so the query is only enabled when
username is truthy.
In `@apps/self-hosted/src/features/tipping/components/tipping-popover.tsx`:
- Around line 126-133: The dialog element in tipping-popover (the div using
refs.setFloating with role="dialog") needs aria-modal="true" and proper focus
management; add aria-modal="true" to that div and implement focus trapping: on
open move focus into the dialog (e.g., focus the first tabbable element or a
dialog container) and on close return focus to the trigger element (the element
that called refs.setFloating), or use a focus-trap utility/hook; ensure focus is
restored on unmount and keyboard/escape handling still closes the popover.
- Around line 43-45: Change the selectedAsset state to use the TippingAsset type
(useState<TippingAsset | undefined>) instead of string so consumers get correct
typing; update the selectedAsset declaration (selectedAsset, setSelectedAsset)
to use that generic type, remove the unsafe cast "selectedAsset as TippingAsset"
where it's used (e.g. in the submit flow), and delete the now-unnecessary
explicit `"HIVE" | "HBD" | "POINTS"` runtime guard inside handleSubmit since the
typed state makes it unreachable.
In `@apps/self-hosted/src/features/tipping/components/tipping-step-amount.tsx`:
- Around line 28-29: The preset amount buttons in the TippingStepAmount
component render a hardcoded "$" (the `${val}` button text) which can mislead
users into thinking the payment is in USD; update the button label to explicitly
indicate these are USD equivalents (e.g. change the visible text from `$${val}`
to `$${val} USD` or `$${val} USD equivalent`) and also update the button's
aria-label/tooltip in the same component to mention conversion to HIVE/HBD so
screen readers and hover help are accurate (adjust any occurrences of `val`
rendering in TippingStepAmount and related tooltip/aria props accordingly).
In `@apps/self-hosted/src/features/tipping/components/tipping-step-currency.tsx`:
- Around line 117-118: The button currently renders both messages because the
ternary is evaluated after the &&, so when user?.username is falsy you still hit
the ternary false branch; fix the JSX in the tipping-step-currency component by
making the intent explicit: replace the two separate expressions with a single
conditional that first checks user?.username and returns t("tip_login_to_send")
when missing, otherwise returns loading ? t("tip_sending") : t("tip_send");
locate the fragment using user and loading in tipping-step-currency.tsx and
update that expression (or add parentheses to enforce (user?.username ? (loading
? ... : ...) : ...)).
In `@apps/self-hosted/src/features/tipping/components/tipping-wallet-qr.tsx`:
- Around line 30-32: In the QR generation .catch handler in
tipping-wallet-qr.tsx replace the use of the raw library error message with the
translated user-facing string: always call setError(t("tip_qr_failed")) (instead
of setError(err instanceof Error ? err.message : t("tip_qr_failed"))), so the
error shown to users comes from t("tip_qr_failed") regardless of the thrown err
in the .catch block that follows the qrcode call.
- Around line 50-56: Replace the hardcoded English alt text in the <img> inside
the tipping-wallet-qr component with a localized string; use the existing i18n
key "tip_wallet_address" (or add a new "tip_qr_alt" if preferred) and pass the
translated value as the alt prop on the <img> that renders dataUrl (refer to the
component/rendering of the <img> with props dataUrl, size, className) so the QR
image uses the locale-specific alt text.
- Around line 21-33: The effect that calls QRCode.toDataURL(address, { width:
size, margin: 2 }) can race: add a cancellation guard inside the useEffect to
ignore results from stale promises — e.g., create a local let cancelled = false
(or an incrementing requestId) at the top of the effect, check cancelled before
calling setDataUrl/setError in the promise resolution/rejection, and set
cancelled = true in the effect cleanup; ensure you still handle the
empty-address branch and clear data/error as currently done so only the latest
address/size updates the state via setDataUrl and setError.
In `@apps/self-hosted/src/features/tipping/utils/tip-transaction.ts`:
- Line 29: The variable formatted (const formatted = num.toFixed(3)) is unused
and realAmount is being embedded as a full-precision float into the Hive
transfer amount strings; replace those interpolations to use a 3-decimal string
(realAmount.toFixed(3)) for all Hive amount constructions, and either remove
formatted or repurpose it for the POINTS path once that conversion is fixed;
update the three places where amounts are built (the strings that currently
interpolate realAmount) to use realAmount.toFixed(3) and eliminate the dead
formatted variable if not reused.
- Line 7: ASSETS_WITH_USD_PRICE should be exported and used as a guard so USD
price conversion only runs for assets that need it: change the declaration to
export const ASSETS_WITH_USD_PRICE = ["HIVE", "HBD"]; then wrap the
ensureQueryData(...) call and the amount conversion (currently doing num /
price) in a conditional that checks if the asset is included in
ASSETS_WITH_USD_PRICE (e.g., if (!ASSETS_WITH_USD_PRICE.includes(asset)) skip
fetching prices and leave the numeric amount unchanged); update ensureQueryData
usage and the division logic inside the function handling tip conversion so
POINTS bypasses price lookup and conversion.
- Around line 16-20: Update the JSDoc for the tip execution block to reference
the correct helper: replace the non-existent getTokenPriceQueryOptions with
getAccountWalletAssetInfoQueryOptions so the comment matches the actual call in
this file (referencing getAccountWalletAssetInfoQueryOptions and the tip
execution behavior that fetches token price/info for conversion).
- Line 37: The division uses "realAmount = num / (info?.price ?? 0)" which can
divide by zero; before performing this division check that info exists and
info.price is a positive non-zero number (e.g., info?.price > 0). If the price
is missing or zero, handle it explicitly: throw or return a meaningful error,
log and abort the transfer, or set realAmount to a safe default and skip
creating the transfer string. Update the code around the realAmount calculation
(the realAmount assignment and the subsequent transfer amount string
construction) to validate info?.price and implement the chosen error/abort path
to avoid producing "Infinity HIVE".
---
Outside diff comments:
In `@apps/self-hosted/package.json`:
- Around line 14-65: Update the dependency versions in package.json to match the
web app: change "@floating-ui/dom" to "^1.6.13", "@floating-ui/react-dom" to
"^2.1.2", and "qrcode" to "^1.5.3"; also remove the devDependency
"@rsbuild/plugin-node-polyfill" unless you have a documented reason to keep it
so the self-hosted app remains architecturally aligned with the web app.
In `@apps/self-hosted/src/features/auth/auth-actions.ts`:
- Around line 119-133: The switch in broadcast (auth-actions.ts) drops
authorityType for non-keychain logins; update the branches that call
broadcastWithHivesigner and broadcastWithHiveAuth to validate authorityType and
throw a clear, actionable error when authorityType === "Active" (or
ASSETS_REQUIRING_KEY) instead of proceeding (so callers get a user-friendly
message rather than a raw chain error). Specifically, inside the "hivesigner"
branch (before calling broadcastWithHivesigner) and the "hiveauth" branch
(before calling broadcastWithHiveAuth) check the passed authorityType/session
and throw a descriptive Error like "Active key required for this operation with
hivesigner/hiveauth" if an active-level operation is attempted; also add a short
comment documenting the limitation so callers can gate tipping UI accordingly.
---
Nitpick comments:
In `@apps/self-hosted/package.json`:
- Around line 14-15: Replace the manual open-state and mousedown outside-click
logic in tip-button.tsx and tipping-popover.tsx with Floating UI's interaction
hooks: change the dependency from "@floating-ui/react-dom" to
"@floating-ui/react" in package.json, import useFloating plus useClick,
useDismiss and useInteractions from '@floating-ui/react', and wire them into the
components (useClick to toggle open, useDismiss to handle outside clicks/escape,
useInteractions to combine handlers). Remove the manual useState-based toggle
and the document 'mousedown' listener/cleanup in tip-button.tsx and ensure the
floating context/props from useFloating are passed through to
tipping-popover.tsx so the built-in dismissal and click behavior replace the
existing custom logic.
In `@apps/self-hosted/rsbuild.config.ts`:
- Around line 20-22: The bip39 aliases are duplicated between resolve.alias and
tools.rspack.resolve.alias (the keys 'bip39' and 'bip39-original'); remove the
redundant pair from one layer so aliases live only in the same layer as the
React aliases for consistency—either keep them in resolve.alias (rsbuild-level)
and delete them from tools.rspack.resolve.alias, or vice versa; update only the
entries named 'bip39' and 'bip39-original' and ensure the shim path
(src/shim-bip39.ts) and bip39Resolved symbol remain referenced in the surviving
alias.
- Line 16: The pluginNodePolyfill invocation in the plugins array is currently
unscoped and injects many unnecessary polyfills; update the plugins entry that
references pluginNodePolyfill (in the plugins array) to pass an include option
limited to only the built-ins bip39 needs (e.g., "crypto" and "buffer") so only
those modules are polyfilled and the bundle size is reduced.
In `@apps/self-hosted/src/features/tipping/components/tip-button.tsx`:
- Around line 3-4: Consolidate the Floating UI imports by replacing the split
imports with a single import from "@floating-ui/react-dom" that includes
autoUpdate, offset, flip, shift, and useFloating so that the
whileElementsMounted option receives the correct autoUpdate implementation;
update the import statement to import all those symbols (autoUpdate, offset,
flip, shift, useFloating) from "@floating-ui/react-dom".
In `@apps/self-hosted/src/features/tipping/components/tipping-popover.tsx`:
- Around line 99-106: Remove the redundant asset-check in handleSubmit: since
canSubmit already ensures !isExternalAsset and selectedAsset should be narrowed
to TippingAsset | undefined, delete the block that checks selectedAsset against
"HIVE"/"HBD"/"POINTS" and the setError("This asset is not supported for tipping
yet") return; instead rely on the existing canSubmit/isExternalAsset narrowing
(and fix the selectedAsset type to TippingAsset | undefined where noted) so
handleSubmit no longer contains dead-code guarding unsupported assets.
In `@apps/self-hosted/src/features/tipping/components/tipping-step-amount.tsx`:
- Around line 21-30: presetAmounts can contain duplicates which leads to
duplicate React keys and identical buttons; before rendering in
tipping-step-amount.tsx, produce a deduplicated list (e.g., derive uniqueAmounts
from presetAmounts using a Set or filter by indexOf) and map over uniqueAmounts
when creating the <button> elements (referencing presetAmounts, uniqueAmounts,
onSelect and the button key) so each rendered button has a unique key and
duplicate values are not shown.
In `@apps/self-hosted/src/features/tipping/hooks/use-tipping-config.ts`:
- Around line 9-22: The hook calls InstanceConfigManager.getConfig() inside
useMemo but doesn’t include the config in the dependency array and also returns
preset arrays by reference; fix by reading and including the live config value
(result of InstanceConfigManager.getConfig()) in the useMemo deps (or compute
config before useMemo and include that variable in deps) so changes retrigger
recomputation, and ensure returned presetAmounts and DEFAULT_PRESETS are
defensive copies (e.g., copy tipping.amounts or DEFAULT_PRESETS before
returning) to avoid exposing shared mutable arrays; update references to
InstanceConfigManager.getConfig, useMemo, DEFAULT_PRESETS, tipping.amounts and
variant accordingly.
In `@apps/self-hosted/src/features/tipping/types.ts`:
- Around line 3-9: Replace the duplicated runtime and type definitions by making
TIPABLE_ASSETS the single source of truth (declare const TIPABLE_ASSETS =
["HIVE","HBD","POINTS"] as const) and derive the TippingAsset union from it
(type TippingAsset = typeof TIPABLE_ASSETS[number]); then update isTipableAsset
to use TIPABLE_ASSETS.includes(asset as TippingAsset) exactly as before (keeping
the type predicate signature isTipableAsset(asset: string): asset is
TippingAsset) so compile-time and runtime values cannot diverge; ensure you
remove the old explicit union declaration and export the derived types/names
unchanged.
In `@apps/self-hosted/src/shim-bip39.ts`:
- Around line 8-13: Replace the five/fewer explicit re-exports
(generateMnemonic, mnemonicToSeedSync, mnemonicToSeed, entropyToMnemonic,
mnemonicToEntropy, validateMnemonic) with a single module re-export so the shim
forwards the entire bip39-original public API (including wordlists,
setDefaultWordlist, getDefaultWordlist); update the export statement to use a
wildcard export from 'bip39-original' so future additions to that module are
automatically exposed without changing this file.
| tipping?: { | ||
| enabled?: boolean; | ||
| general?: { enabled: boolean; buttonLabel?: string }; | ||
| post?: { enabled: boolean; buttonLabel?: string }; | ||
| amounts?: number[]; | ||
| }; |
There was a problem hiding this comment.
Top-level tipping.enabled is a schema orphan.
The enabled?: boolean at the tipping root level is absent from both config.template.json and config-fields.ts. The per-variant general.enabled and post.enabled are the actual feature toggles, making the top-level field redundant and potentially confusing to anyone reading the type.
🔧 Suggested cleanup
tipping?: {
- enabled?: boolean;
general?: { enabled: boolean; buttonLabel?: string };
post?: { enabled: boolean; buttonLabel?: string };
amounts?: number[];
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| tipping?: { | |
| enabled?: boolean; | |
| general?: { enabled: boolean; buttonLabel?: string }; | |
| post?: { enabled: boolean; buttonLabel?: string }; | |
| amounts?: number[]; | |
| }; | |
| tipping?: { | |
| general?: { enabled: boolean; buttonLabel?: string }; | |
| post?: { enabled: boolean; buttonLabel?: string }; | |
| amounts?: number[]; | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/self-hosted/src/core/configuration-loader.ts` around lines 71 - 76, The
top-level optional property tipping.enabled is an orphan and should be removed:
delete the enabled?: boolean from the tipping type in configuration-loader.ts so
the shape only contains general, post and amounts (with general.enabled and
post.enabled as the feature toggles); update the corresponding schema/template
(config.template.json) and the config-fields.ts to remove references to
tipping.enabled so runtime validation and UI fields align with the type.
apps/self-hosted/src/features/tipping/components/tipping-currency-card.tsx
Show resolved
Hide resolved
| <button | ||
| key={asset} | ||
| type="button" | ||
| className={clsx( | ||
| "flex items-center gap-2 px-3 py-2 rounded-md border text-sm", | ||
| selectedAsset === asset | ||
| ? "border-theme-accent bg-theme-tertiary text-theme-primary" | ||
| : "border-theme bg-theme-primary text-theme-primary hover:bg-theme-tertiary", | ||
| )} | ||
| onClick={() => onAssetSelect(asset)} | ||
| > | ||
| {data?.title} | ||
| </button> |
There was a problem hiding this comment.
Two minor issues: redundant key prop and empty loading state on the button.
-
key={asset}on Line 23 is a no-op —keyonly has meaning when the element is placed directly in an array render (e.g. via.map()). The parentTippingCurrencyCardsalready applieskey={asset}correctly at the call site. -
Line 33 renders
{data?.title}which isundefinedwhile the query is loading, resulting in a blank button. Consider showing the rawassetsymbol as a fallback:
♻️ Proposed fix
return (
<button
- key={asset}
type="button"
...
>
- {data?.title}
+ {data?.title ?? asset}
</button>
);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/self-hosted/src/features/tipping/components/tipping-currency-card.tsx`
around lines 22 - 34, Remove the redundant key prop on the button (it's a no-op
here because the parent TippingCurrencyCards already assigns key={asset}) and
update the button label to fall back to the raw asset symbol when data?.title is
undefined; specifically, change the button rendering in the component that uses
selectedAsset, onAssetSelect and data?.title to display data?.title || asset so
the button isn't blank while the query is loading.
apps/self-hosted/src/features/tipping/components/tipping-currency-cards.tsx
Show resolved
Hide resolved
| import type { Operation } from "@hiveio/dhive"; | ||
| import type { TippingAsset } from "../types"; | ||
|
|
||
| const ASSETS_WITH_USD_PRICE: TippingAsset[] = ["HIVE", "HBD"]; |
There was a problem hiding this comment.
ASSETS_WITH_USD_PRICE is defined but never used as a guard — POINTS incorrectly goes through USD price conversion.
ASSETS_WITH_USD_PRICE = ["HIVE", "HBD"] clearly signals that POINTS should bypass USD-based price conversion, yet the ensureQueryData call and the num / price division at line 37 execute unconditionally for every asset, including POINTS. This means a user entering "100 POINTS" would send 100 / pointsUsdPrice points instead of 100.
The constant should be used as a branch condition, and the price-fetch block should only run for ASSETS_WITH_USD_PRICE assets. The constant also needs export to match its declared public role (the AI-generated summary lists it as an export, and the tipping UI will need it for display logic).
♻️ Proposed restructure
-const ASSETS_WITH_USD_PRICE: TippingAsset[] = ["HIVE", "HBD"];
+export const ASSETS_WITH_USD_PRICE: TippingAsset[] = ["HIVE", "HBD"];
// ...inside executeTip...
- const queryClient = getQueryClient();
- const info = await queryClient.ensureQueryData(
- getAccountWalletAssetInfoQueryOptions(to, asset),
- );
- realAmount = num / (info?.price ?? 0);
+ if ((ASSETS_WITH_USD_PRICE as TippingAsset[]).includes(asset)) {
+ const queryClient = getQueryClient();
+ const info = await queryClient.ensureQueryData(
+ getAccountWalletAssetInfoQueryOptions(to, asset),
+ );
+ const price = info?.price;
+ if (!price || price <= 0) {
+ throw new Error("Asset price unavailable");
+ }
+ realAmount = num / price;
+ } else {
+ realAmount = num; // POINTS: user enters the POINTS quantity directly
+ }Also applies to: 34-36
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/self-hosted/src/features/tipping/utils/tip-transaction.ts` at line 7,
ASSETS_WITH_USD_PRICE should be exported and used as a guard so USD price
conversion only runs for assets that need it: change the declaration to export
const ASSETS_WITH_USD_PRICE = ["HIVE", "HBD"]; then wrap the
ensureQueryData(...) call and the amount conversion (currently doing num /
price) in a conditional that checks if the asset is included in
ASSETS_WITH_USD_PRICE (e.g., if (!ASSETS_WITH_USD_PRICE.includes(asset)) skip
fetching prices and leave the numeric amount unchanged); update ensureQueryData
usage and the division logic inside the function handling tip conversion so
POINTS bypasses price lookup and conversion.
| if (!Number.isFinite(num) || num <= 0) { | ||
| throw new Error("Invalid amount"); | ||
| } | ||
| const formatted = num.toFixed(3); |
There was a problem hiding this comment.
formatted is dead code and realAmount is embedded as a raw float — Hive rejects non-3-decimal amounts.
const formatted = num.toFixed(3) (line 29) is never used anywhere. Meanwhile, the blockchain amount strings on lines 48, 60, and 74 interpolate the raw realAmount float (e.g., "1.4285714285714286 HIVE"). The Hive transfer operation requires the amount "to be a positive value with 3 decimals in order for the transaction to be completed." A full-precision float will be rejected by the node.
realAmount.toFixed(3) must be used in all three amount strings. The formatted variable should either be repurposed for the POINTS direct-input path (once the POINTS price-conversion issue below is resolved) or removed.
🐛 Proposed fix
- const formatted = num.toFixed(3);
// ... price fetch ...
if (asset === "HIVE") {
operations = [[
"transfer",
{
from, to,
- amount: `${realAmount} HIVE`,
+ amount: `${realAmount.toFixed(3)} HIVE`,
memo,
},
]];
} else if (asset === "HBD") {
operations = [[
"transfer",
{
from, to,
- amount: `${realAmount} HBD`,
+ amount: `${realAmount.toFixed(3)} HBD`,
memo,
},
]];
} else if (asset === "POINTS") {
operations = [[
"custom_json",
{
// ...
- amount: `${realAmount} POINT`,
+ amount: `${num.toFixed(3)} POINT`, // POINTS: use input directly (see ASSETS_WITH_USD_PRICE note)
memo,
},
]];
}Also applies to: 48-48, 60-60, 74-74
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/self-hosted/src/features/tipping/utils/tip-transaction.ts` at line 29,
The variable formatted (const formatted = num.toFixed(3)) is unused and
realAmount is being embedded as a full-precision float into the Hive transfer
amount strings; replace those interpolations to use a 3-decimal string
(realAmount.toFixed(3)) for all Hive amount constructions, and either remove
formatted or repurpose it for the POINTS path once that conversion is fixed;
update the three places where amounts are built (the strings that currently
interpolate realAmount) to use realAmount.toFixed(3) and eliminate the dead
formatted variable if not reused.
| const info = await queryClient.ensureQueryData( | ||
| getAccountWalletAssetInfoQueryOptions(to, asset), | ||
| ); | ||
| realAmount = num / (info?.price ?? 0); |
There was a problem hiding this comment.
Division by zero when price is unavailable produces Infinity as the transfer amount.
info?.price ?? 0 resolves to 0 whenever the query returns no data or a zero price, making realAmount = Infinity. This propagates directly into the blockchain operation amount string (e.g., "Infinity HIVE"), which will either be rejected by the node or exhibit undefined behaviour. A hard guard is required before dividing.
🐛 Proposed fix
- realAmount = num / (info?.price ?? 0);
+ const price = info?.price;
+ if (!price || price <= 0) {
+ throw new Error("Asset price unavailable");
+ }
+ realAmount = num / price;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| realAmount = num / (info?.price ?? 0); | |
| const price = info?.price; | |
| if (!price || price <= 0) { | |
| throw new Error("Asset price unavailable"); | |
| } | |
| realAmount = num / price; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/self-hosted/src/features/tipping/utils/tip-transaction.ts` at line 37,
The division uses "realAmount = num / (info?.price ?? 0)" which can divide by
zero; before performing this division check that info exists and info.price is a
positive non-zero number (e.g., info?.price > 0). If the price is missing or
zero, handle it explicitly: throw or return a meaningful error, log and abort
the transfer, or set realAmount to a safe default and skip creating the transfer
string. Update the code around the realAmount calculation (the realAmount
assignment and the subsequent transfer amount string construction) to validate
info?.price and implement the chosen error/abort path to avoid producing
"Infinity HIVE".
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/self-hosted/src/core/i18n.ts (1)
158-218:⚠️ Potential issue | 🔴 CriticalTypeScript compilation error:
es,de,ko,rulocales are missing all 14 new translation keys.
TranslationsisRecord<TranslationKey, string>, so every locale object must include every key in the union. The four localeses,de,ko,ruwere not updated with the newtip_*andcancelkeys, which will cause a TS compile-time type error. At runtime thet()fallback would silently serve English strings to non-English users, but the build should fail first.All four locales need the following keys added (translations are placeholders — verify with a native speaker):
🐛 Proposed additions for `es`, `de`, `ko`, `ru`
// es block — after community_not_found + tip_amount: "Cantidad", + tip_custom: "Personalizado", + tip_currency: "Moneda", + tip_private_key: "Clave activa", + tip_wallet_address: "Dirección de billetera", + tip_no_wallet_address: "El destinatario no ha configurado esta dirección.", + tip_send: "Propina", + tip_sending: "Enviando...", + tip_login_to_send: "Inicia sesión para enviar una propina", + tip_asset_not_supported: "Este activo aún no es compatible para propinas", + tip_transaction_failed: "Transacción fallida", + tip_qr_no_address: "Sin dirección", + tip_qr_failed: "Error al generar el QR", + cancel: "Cancelar", // de block — after community_not_found + tip_amount: "Betrag", + tip_custom: "Benutzerdefiniert", + tip_currency: "Währung", + tip_private_key: "Aktiver Schlüssel", + tip_wallet_address: "Wallet-Adresse", + tip_no_wallet_address: "Empfänger hat diese Wallet-Adresse nicht eingerichtet.", + tip_send: "Trinkgeld", + tip_sending: "Wird gesendet...", + tip_login_to_send: "Anmelden, um ein Trinkgeld zu senden", + tip_asset_not_supported: "Dieses Asset wird für Trinkgelder noch nicht unterstützt", + tip_transaction_failed: "Transaktion fehlgeschlagen", + tip_qr_no_address: "Keine Adresse", + tip_qr_failed: "QR-Generierung fehlgeschlagen", + cancel: "Abbrechen", // ko block — after community_not_found + tip_amount: "금액", + tip_custom: "사용자 정의", + tip_currency: "통화", + tip_private_key: "활성 키", + tip_wallet_address: "지갑 주소", + tip_no_wallet_address: "수신자가 이 지갑 주소를 설정하지 않았습니다.", + tip_send: "팁 보내기", + tip_sending: "전송 중...", + tip_login_to_send: "팁을 보내려면 로그인하세요", + tip_asset_not_supported: "이 자산은 아직 팁 기능을 지원하지 않습니다", + tip_transaction_failed: "트랜잭션 실패", + tip_qr_no_address: "주소 없음", + tip_qr_failed: "QR 생성 실패", + cancel: "취소", // ru block — after community_not_found + tip_amount: "Сумма", + tip_custom: "Произвольная", + tip_currency: "Валюта", + tip_private_key: "Активный ключ", + tip_wallet_address: "Адрес кошелька", + tip_no_wallet_address: "Получатель не настроил этот адрес кошелька.", + tip_send: "Чаевые", + tip_sending: "Отправка...", + tip_login_to_send: "Войдите, чтобы отправить чаевые", + tip_asset_not_supported: "Этот актив ещё не поддерживается для чаевых", + tip_transaction_failed: "Транзакция не удалась", + tip_qr_no_address: "Нет адреса", + tip_qr_failed: "Не удалось создать QR", + cancel: "Отмена",Also applies to: 219-279, 356-416, 417-477
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/self-hosted/src/core/i18n.ts` around lines 158 - 218, The es, de, ko and ru locale objects are missing the 14 new translation keys (the tip_* keys and cancel) required by the Translations type, causing a TypeScript compile error; update each locale object (es, de, ko, ru) inside the i18n export to include all new keys (use placeholder strings if unsure) so each Record<TranslationKey, string> contains every key in the union, ensuring functions like t() compile; locate the locale objects named "es", "de", "ko", "ru" and add the missing tip_* and cancel entries consistently wherever those locale blocks appear (e.g., the blocks around the shown diff ranges).
🧹 Nitpick comments (2)
apps/self-hosted/src/core/i18n.ts (1)
137-156: Mixed quote style within theenblock.Lines 137–142 retain single-quoted strings (
cant_reblog_ownthroughcommunity_not_found), while the new tip keys (lines 143–156) use double quotes. The same inconsistency exists in thefrblock (lines 339–340 vs. 342–354).♻️ Normalise to double quotes (matching the new keys)
- cant_reblog_own: "You can't reblog your own post", - already_reblogged: 'Already reblogged', - reblog_to_followers: 'Reblog to your followers', - error_loading: 'Something went wrong. Please try again.', - retry: 'Retry', - community_not_found: 'Community not found.', + cant_reblog_own: "You can't reblog your own post", + already_reblogged: "Already reblogged", + reblog_to_followers: "Reblog to your followers", + error_loading: "Something went wrong. Please try again.", + retry: "Retry", + community_not_found: "Community not found.",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/self-hosted/src/core/i18n.ts` around lines 137 - 156, Convert all string literals in the en and fr locale blocks to use double quotes for consistency: replace single-quoted values for keys like cant_reblog_own, already_reblogged, reblog_to_followers, error_loading, retry, community_not_found with double-quoted equivalents so they match the new tip_* keys (tip_amount, tip_custom, tip_currency, tip_private_key, tip_wallet_address, tip_no_wallet_address, tip_send, tip_sending, tip_login_to_send, tip_asset_not_supported, tip_transaction_failed, tip_qr_no_address, tip_qr_failed, cancel); do the same normalization in the fr block for the corresponding translated keys to ensure consistent double-quote usage across both locale objects.apps/self-hosted/src/features/auth/auth-actions.ts (1)
102-102: JSDoc@paramname doesn't match the actual parameter.The comment uses
@param authorityTypebut the function's second parameter is namedoptions. This will produce incorrect IDE hover docs. Use the nested-property JSDoc form instead.📝 Proposed fix
- * `@param` authorityType - For keychain: which key to use (e.g. "Active" for transfers). + * `@param` options - Optional broadcast settings. + * `@param` options.authorityType - For keychain: which key to use (e.g. "Active" for transfers). Ignored for hivesigner and hiveauth.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/self-hosted/src/features/auth/auth-actions.ts` at line 102, The JSDoc `@param` currently names authorityType but the function actually takes an options parameter; update the JSDoc to use the nested-property form (e.g. `@param` options.authorityType - For keychain: which key to use (e.g. "Active" for transfers).) and remove or replace the incorrect `@param` authorityType entry so the docs match the function signature and IDE hover shows the correct description for options.authorityType; ensure the JSDoc references the existing parameter name "options" and the property "authorityType" to locate the doc for the function in auth-actions.ts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/self-hosted/package.json`:
- Around line 14-15: The web app's package.json must be aligned to the
self-hosted package versions: update the web app package.json dependencies to
use "@floating-ui/dom": "^1.7.4", "@floating-ui/react-dom": "^2.1.6", and
"qrcode": "^1.5.4" (these are required because files like
apps/self-hosted/src/features/tipping/components/tip-button.tsx import
autoUpdate and offset from "@floating-ui/dom"), and either add the devDependency
"@rsbuild/plugin-node-polyfill" to the web app devDependencies or add a short
justification comment in the PR explaining why the web app should intentionally
keep a different set; ensure package.json keys are updated (not duplicate) and
run install to verify no resolution conflicts.
In `@apps/self-hosted/src/core/i18n.ts`:
- Line 143: The JSON/locales object has a stray tab before the "tip_amount"
entry in the English and French blocks causing inconsistent indentation; locate
the "tip_amount" key in the i18n exports (the entries under the "en" and "fr"
locale objects) and replace the leading tab character with the same number of
spaces used by surrounding lines so indentation matches the rest of the file
(update both occurrences referenced by the "tip_amount" symbol).
In `@apps/self-hosted/src/features/auth/auth-actions.ts`:
- Around line 114-118: Export the BroadcastAuthorityType from the auth module's
index.ts so callers can import it from the module root (add
BroadcastAuthorityType to the type exports alongside existing exports), and add
a validation in auth-actions.ts around the computed authorityType (variable
authorityType and the switch on user.loginType) that detects when authorityType
!== "Posting" but user.loginType is not "keychain" (e.g., hivesigner or
hiveauth) and either log a dev-time warning or return an error/early rejection
to prevent the passed authority being silently ignored; reference the
keychainBroadcast call and the switch handling of "hivesigner" / "hiveauth" when
inserting this guard.
---
Outside diff comments:
In `@apps/self-hosted/src/core/i18n.ts`:
- Around line 158-218: The es, de, ko and ru locale objects are missing the 14
new translation keys (the tip_* keys and cancel) required by the Translations
type, causing a TypeScript compile error; update each locale object (es, de, ko,
ru) inside the i18n export to include all new keys (use placeholder strings if
unsure) so each Record<TranslationKey, string> contains every key in the union,
ensuring functions like t() compile; locate the locale objects named "es", "de",
"ko", "ru" and add the missing tip_* and cancel entries consistently wherever
those locale blocks appear (e.g., the blocks around the shown diff ranges).
---
Nitpick comments:
In `@apps/self-hosted/src/core/i18n.ts`:
- Around line 137-156: Convert all string literals in the en and fr locale
blocks to use double quotes for consistency: replace single-quoted values for
keys like cant_reblog_own, already_reblogged, reblog_to_followers,
error_loading, retry, community_not_found with double-quoted equivalents so they
match the new tip_* keys (tip_amount, tip_custom, tip_currency, tip_private_key,
tip_wallet_address, tip_no_wallet_address, tip_send, tip_sending,
tip_login_to_send, tip_asset_not_supported, tip_transaction_failed,
tip_qr_no_address, tip_qr_failed, cancel); do the same normalization in the fr
block for the corresponding translated keys to ensure consistent double-quote
usage across both locale objects.
In `@apps/self-hosted/src/features/auth/auth-actions.ts`:
- Line 102: The JSDoc `@param` currently names authorityType but the function
actually takes an options parameter; update the JSDoc to use the nested-property
form (e.g. `@param` options.authorityType - For keychain: which key to use (e.g.
"Active" for transfers).) and remove or replace the incorrect `@param`
authorityType entry so the docs match the function signature and IDE hover shows
the correct description for options.authorityType; ensure the JSDoc references
the existing parameter name "options" and the property "authorityType" to locate
the doc for the function in auth-actions.ts.
| "@floating-ui/dom": "^1.7.4", | ||
| "@floating-ui/react-dom": "^2.1.6", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check for direct imports of `@floating-ui/dom` in tipping code
rg -n --type ts --type tsx "from ['\"]@floating-ui/dom['\"]" || echo "No direct imports found"Repository: ecency/vision-next
Length of output: 130
🏁 Script executed:
#!/bin/bash
# Locate web app package.json (typically at apps/web or similar)
find . -name "package.json" -type f | grep -E "(web|app)" | head -10Repository: ecency/vision-next
Length of output: 527
🏁 Script executed:
#!/bin/bash
# List all package.json files in the repository
find . -name "package.json" -type f | head -20Repository: ecency/vision-next
Length of output: 1236
🏁 Script executed:
#!/bin/bash
# Read web app package.json dependencies
cat ./apps/web/package.json | grep -A 50 '"dependencies"' | head -80Repository: ecency/vision-next
Length of output: 2028
🏁 Script executed:
#!/bin/bash
# Broader search for `@floating-ui/dom` usage with adjusted pattern
rg "@floating-ui/dom" --type ts -n || echo "No `@floating-ui/dom` references found"Repository: ecency/vision-next
Length of output: 978
🏁 Script executed:
#!/bin/bash
# Check if `@floating-ui/dom`, qrcode, `@rsbuild/plugin-node-polyfill`, `@types/qrcode` are in web app
cat ./apps/web/package.json | grep -E '"(`@floating-ui`|qrcode|@rsbuild/plugin-node-polyfill|@types/qrcode)"'Repository: ecency/vision-next
Length of output: 116
🏁 Script executed:
#!/bin/bash
# Read web app devDependencies
cat ./apps/web/package.json | grep -A 100 '"devDependencies"' | grep -E '(`@rsbuild/plugin-node-polyfill`|@types/qrcode)'Repository: ecency/vision-next
Length of output: 92
🏁 Script executed:
#!/bin/bash
# Also check self-hosted versions to compare side-by-side
echo "=== Self-hosted versions ===" && \
cat ./apps/self-hosted/package.json | grep -E '"(`@floating-ui`|qrcode|@rsbuild|@types/qrcode)"' && \
echo "" && echo "=== Web app versions ===" && \
cat ./apps/web/package.json | grep -E '"(`@floating-ui`|qrcode|@rsbuild|@types/qrcode)"'Repository: ecency/vision-next
Length of output: 226
Align dependency versions between self-hosted and web app packages.
The @floating-ui/dom dependency is not redundant—it's directly imported in apps/self-hosted/src/features/tipping/components/tip-button.tsx (e.g., import { autoUpdate, offset } from "@floating-ui/dom"), and multiple files in the web app also import directly from it.
However, the self-hosted package has version mismatches with the web app:
@floating-ui/dom: self-hosted uses^1.7.4while web app uses^1.6.13@floating-ui/react-dom: self-hosted uses^2.1.6while web app uses^2.1.2qrcode: self-hosted uses^1.5.4while web app uses^1.5.3@rsbuild/plugin-node-polyfill: added to self-hosted devDeps but not present in web app
Update the web app's package.json to match self-hosted versions, or justify version differences if intentional.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/self-hosted/package.json` around lines 14 - 15, The web app's
package.json must be aligned to the self-hosted package versions: update the web
app package.json dependencies to use "@floating-ui/dom": "^1.7.4",
"@floating-ui/react-dom": "^2.1.6", and "qrcode": "^1.5.4" (these are required
because files like
apps/self-hosted/src/features/tipping/components/tip-button.tsx import
autoUpdate and offset from "@floating-ui/dom"), and either add the devDependency
"@rsbuild/plugin-node-polyfill" to the web app devDependencies or add a short
justification comment in the PR explaining why the web app should intentionally
keep a different set; ensure package.json keys are updated (not duplicate) and
run install to verify no resolution conflicts.
| error_loading: 'Something went wrong. Please try again.', | ||
| retry: 'Retry', | ||
| community_not_found: 'Community not found.', | ||
| tip_amount: "Amount", |
There was a problem hiding this comment.
Stray tab character causes inconsistent indentation.
Lines 143 and 341 (tip_amount in en and fr) are indented with a tab while every surrounding line uses spaces.
✏️ Proposed fix
- tip_amount: "Amount",
+ tip_amount: "Amount",- tip_amount: "Montant",
+ tip_amount: "Montant",Also applies to: 341-341
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/self-hosted/src/core/i18n.ts` at line 143, The JSON/locales object has a
stray tab before the "tip_amount" entry in the English and French blocks causing
inconsistent indentation; locate the "tip_amount" key in the i18n exports (the
entries under the "en" and "fr" locale objects) and replace the leading tab
character with the same number of spaces used by surrounding lines so
indentation matches the rest of the file (update both occurrences referenced by
the "tip_amount" symbol).
| const authorityType = options?.authorityType ?? "Posting"; | ||
|
|
||
| switch (user.loginType) { | ||
| case "keychain": | ||
| return keychainBroadcast(user.username, operations); | ||
| return keychainBroadcast(user.username, operations, authorityType); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat apps/self-hosted/src/features/auth/index.tsRepository: ecency/vision-next
Length of output: 1265
🏁 Script executed:
sed -n '110,150p' apps/self-hosted/src/features/auth/auth-actions.tsRepository: ecency/vision-next
Length of output: 744
Add BroadcastAuthorityType to type exports in index.ts; add validation for non-keychain paths receiving non-default authorityType.
-
BroadcastAuthorityTypeis not re-exported from the auth module's index, requiring callers to use a deep import. Add it to the type exports to maintain API consistency. -
The
authorityTypeparameter (default: "Posting") is computed on line 114 but silently discarded forhivesignerandhiveauthlogin types (lines 124, 130). Consider adding a dev-time warning or early return guard when a non-defaultauthorityTypeis passed with a non-keychain login type to prevent subtle bugs.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/self-hosted/src/features/auth/auth-actions.ts` around lines 114 - 118,
Export the BroadcastAuthorityType from the auth module's index.ts so callers can
import it from the module root (add BroadcastAuthorityType to the type exports
alongside existing exports), and add a validation in auth-actions.ts around the
computed authorityType (variable authorityType and the switch on user.loginType)
that detects when authorityType !== "Posting" but user.loginType is not
"keychain" (e.g., hivesigner or hiveauth) and either log a dev-time warning or
return an error/early rejection to prevent the passed authority being silently
ignored; reference the keychainBroadcast call and the switch handling of
"hivesigner" / "hiveauth" when inserting this guard.
Summary by CodeRabbit
New Features
Configuration
Internationalization