Hosting Earnings UX Improvement#772
Conversation
…allet When the wHOT wallet fails due to missing membrane proof (host not registered), show a clear 'Registration required' message instead of the internal error string 'No membrane proof stored — call setUnytMembraneProof first'. Hide balance, pubkey, and withdraw sections entirely until registration is complete, rather than showing error strings as field values.
When a host has not yet registered with the hosting index, show a clear call-to-action prompting them to register instead of rendering the Wallet component (which would show membrane proof errors). The CTA links directly to the Hosting Profile registration section.
Show a step-by-step progress indicator when setup is incomplete: 1. Register with hosting index (✅ once logged in) 2. Membrane proof received (⏳ fetching / ✅ done / ❌ error + retry) 3. Unyt DNA installed (⏳ installing / ✅ done) The indicator auto-hides once all steps are complete. Also track Unyt DNA installation status with periodic polling after membrane proof is stored.
Reorder Settings tab sections so infrastructure setup comes first: 1. TLS Configuration 2. Email Configuration (SMTP) 3. Host Index Registration (renamed from 'Hosting Profile') Rename 'Hosting Profile' to 'Host Index Registration' to make it clear this step is required for paid hosting earnings. Update onboarding copy in the paid hosting toggle description and registration form to explain the full registration → earnings flow.
The step-by-step progress UI (register / membrane proof / DNA install) was causing layout issues — it pushed the registration form's code entry field off screen during email verification. Since the membrane proof and DNA install happen automatically in seconds with no user action needed, showing their status adds noise without value. The core UX improvements remain: friendly error messages, gated earnings section, and clear registration prompt.
- Show contextual messages in Earnings section based on current state: - No session: prompt to complete Host Index Registration - Email verification pending: prompt to check inbox - Membrane proof pending or profile incomplete: prompt to fill in details - All complete: render Wallet component - Remove needsRegistration/isMembraneProofError logic from Wallet.tsx (now unnecessary since Hosting.tsx gates rendering)
📝 WalkthroughWalkthroughAdds Unyt DNA install tracking and polling to Hosting: marks membrane-proof done when Unyt is present, polls runtime for Unyt installation after proof completion, and gates Wallet rendering on membrane-proof/host registration/Unyt state; minor Wallet error-handling and spinner-size tweaks; reordered Settings sections. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Hosting as Hosting UI
participant Runtime as client.runtime
participant Wallet as Wallet UI
User->>Hosting: Open Hosting page / enable paid hosting
Hosting->>Hosting: check membraneProofStatus
alt membrane proof already satisfied
Hosting-->>Hosting: set membraneProofStatus = "done"\nset unytDnaInstalled = true
else after membraneProofStatus becomes "done"
loop every 5s (up to ~60 attempts)
Hosting->>Runtime: unytVersionInfo()
Runtime-->>Hosting: { installed: true|false, ... }
alt installed == true
Hosting-->>Hosting: set unytDnaInstalled = true\nstop polling
end
end
alt polling times out or errors
Hosting-->>Hosting: set unytInstallFail = true\nstop polling
end
end
Hosting->>Wallet: render Wallet only if gating satisfied
Wallet-->>User: show Wallet UI
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@ui/src/components/Hosting.tsx`:
- Around line 1281-1313: The current conditional collapses membraneProofStatus
=== "error" into the non-ready UI and mounts <Wallet /> as soon as
membraneProofStatus === "done" regardless of unytDnaInstalled, which leaves
users stuck on proof failure and allows Wallet to render before DNA is
installed; update the branching around membraneProofStatus and hostReg.name so
that: 1) membraneProofStatus === "error" renders an explicit error message with
a visible retry action (reuse the existing retry handler/UI from the disabled
block) instead of the generic prompt, and 2) the successful branch only mounts
<Wallet /> when membraneProofStatus === "done" AND unytDnaInstalled is true
(otherwise show a “DNA installing” / progress message); reference
membraneProofStatus, hostReg.name, unytDnaInstalled, and Wallet when making the
changes.
In `@ui/src/components/Wallet.tsx`:
- Around line 153-159: Remove the sensitive console logging in the Wallet
component: eliminate the two console.log calls that print the raw and parsed
wallet history returned by client.runtime.unytWalletHistory (the "Wallet history
raw:" and "Wallet history parsed:" logs) so transaction metadata is not leaked;
keep the JSON.parse and setHistory(parsed) flow but do not log the payload (or
replace with a non-sensitive debug flagged/log level if absolutely needed),
locating these calls near the unytWalletHistory call in Wallet.tsx.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 430f4d3d-5695-45f1-a36a-8889b4133a81
📒 Files selected for processing (2)
ui/src/components/Hosting.tsxui/src/components/Wallet.tsx
…tall - Hosting: add explicit error branch for membraneProofStatus === 'error' with visible error message and retry button - Hosting: gate Wallet mount on unytDnaInstalled, show progress spinner while DNA is installing - Wallet: remove console.log calls that leaked raw wallet history data
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@ui/src/components/Hosting.tsx`:
- Around line 1326-1356: The Earnings card currently renders the raw
membraneProofError when membraneProofStatus === "error"; change this to avoid
exposing backend/internal error strings by replacing the direct render of
membraneProofError with a sanitized user-facing message (e.g., "Failed to
connect to Unyt network" or the output of a new helper like
getFriendlyMembraneError(membraneProofError)). Update the JSX around
membraneProofStatus / membraneProofError in the Hosting component so the UI
shows only the generic message and keep the original error for diagnostics only
(log it to console or send to telemetry inside fetchMembraneProof or a sanitizer
function), and keep the Retry button behavior (onClick -> hostSession &&
fetchMembraneProof(hostSession)) unchanged.
- Around line 1357-1412: The Wallet is being mounted based on the ephemeral
hostReg.name (which becomes truthy on the first keystroke); change the gate so
Wallet only renders when the profile has been persisted (i.e., after
handleUpdateHost completes). Concretely, introduce or use a persisted flag
(e.g., hostRegPersisted, hostReg.id, or a boolean savedHost) that is set when
handleUpdateHost successfully saves the host, and replace checks of hostReg.name
in the ternary (with membraneProofStatus) so the Wallet block only shows when
membraneProofStatus === "done" && persistedFlag is true; keep Wallet key and
existing styling unchanged. Ensure handleUpdateHost sets that persisted flag on
success and any drafts do not flip it.
- Around line 953-969: The polling useEffect (checking
client.runtime.unytVersionInfo inside the anonymous check function) currently
swallows all errors and can poll forever; modify it to detect failure/timeout by
adding a retry/failure counter or timeout state (e.g., unytInstallFail or
unytInstallAttempts) inside the effect, incrementing on catch, and when attempts
exceed a threshold or timeout is reached stop the interval and set a failure
flag (and clearInterval and set cancelled) so the UI can show the retry path;
update the check function and the cleanup return to ensure the interval is
cleared on failure and on unmount, and use the existing names (client,
membraneProofStatus, unytDnaInstalled, check, cancelled) so the change is
localized.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d1eedba9-ee50-4a15-90f8-2bda9d3f2740
📒 Files selected for processing (1)
ui/src/components/Hosting.tsx
- Add retry/timeout logic to Unyt DNA polling useEffect (max 60 attempts) - Sanitize membrane proof error message shown to users - Gate Wallet render on persisted hostData instead of ephemeral hostReg.name
There was a problem hiding this comment.
♻️ Duplicate comments (2)
ui/src/components/Hosting.tsx (2)
954-979:⚠️ Potential issue | 🟠 MajorTimeout/retry flow can stall indefinitely in “Installing Unyt DNA...”
Line 968 increments
attemptsonly insidecatch, so repeated{ installed: false }responses never hit timeout. Also, the Retry handler on Line 1408 togglesunytInstallFail, but the effect doesn’t depend on it, so polling won’t restart.Suggested fix
useEffect(() => { - if (!client || membraneProofStatus !== "done" || unytDnaInstalled) return; + if (!client || membraneProofStatus !== "done" || unytDnaInstalled || unytInstallFail) return; let cancelled = false; let attempts = 0; const MAX_ATTEMPTS = 60; // ~5 minutes at 5s intervals const check = async () => { + attempts++; try { const vi = await client.runtime.unytVersionInfo(); if (vi) { const info = JSON.parse(vi); if (info.installed && !cancelled) setUnytDnaInstalled(true); } - } catch { - attempts++; - if (attempts >= MAX_ATTEMPTS && !cancelled) { - cancelled = true; - clearInterval(interval); - setUnytInstallFail(true); - } - } + } catch { + // continue polling until max attempts + } + if (attempts >= MAX_ATTEMPTS && !cancelled) { + cancelled = true; + clearInterval(interval); + setUnytInstallFail(true); + } }; check(); const interval = setInterval(check, 5000); return () => { cancelled = true; clearInterval(interval); }; -}, [client, membraneProofStatus, unytDnaInstalled]); +}, [client, membraneProofStatus, unytDnaInstalled, unytInstallFail]);Also applies to: 1408-1410
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/src/components/Hosting.tsx` around lines 954 - 979, The polling useEffect that calls client.runtime.unytVersionInfo (inside the useEffect block around the check function) never increments attempts when the call succeeds but returns {installed: false}, so the MAX_ATTEMPTS timeout can stall; update the logic to increment attempts on each non-installed response (i.e., when vi parses but info.installed is false) and ensure attempts/cancelled are reset when starting a new poll; also add the unytInstallFail state (and any retry-triggering state) to the useEffect dependency array so toggling setUnytInstallFail restarts polling, and ensure the interval is cleared and recreated correctly with the new dependencies (references: attempts, MAX_ATTEMPTS, interval, check, unytDnaInstalled, setUnytInstallFail).
1367-1385:⚠️ Potential issue | 🟠 MajorWallet gate should require saved host name, not just
hostDataobject presenceAt Line 1367,
!hostDatais too weak. A saved profile object with emptynamestill passes and allows<Wallet />to mount, even though the UI copy says host details must be filled first.Suggested fix
+const hasSavedHostName = Boolean((hostData?.name ?? "").trim()); - ) : membraneProofStatus !== "done" || !hostData ? ( + ) : membraneProofStatus !== "done" || !hasSavedHostName ? (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/src/components/Hosting.tsx` around lines 1367 - 1385, The guard that allows the <Wallet /> to mount is using a weak check (!hostData); update the condition to require a non-empty saved host name by checking hostData?.name (e.g., !hostData?.name || hostData.name.trim() === "") instead of just !hostData, and use that same stronger check in the JSX branch that shows the Host Index Registration message so the wallet only activates when membraneProofStatus === "done" AND hostData.name is present; locate references to membraneProofStatus and hostData in this component (the ternary controlling the earnings/Wallet rendering) and replace the condition accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@ui/src/components/Hosting.tsx`:
- Around line 954-979: The polling useEffect that calls
client.runtime.unytVersionInfo (inside the useEffect block around the check
function) never increments attempts when the call succeeds but returns
{installed: false}, so the MAX_ATTEMPTS timeout can stall; update the logic to
increment attempts on each non-installed response (i.e., when vi parses but
info.installed is false) and ensure attempts/cancelled are reset when starting a
new poll; also add the unytInstallFail state (and any retry-triggering state) to
the useEffect dependency array so toggling setUnytInstallFail restarts polling,
and ensure the interval is cleared and recreated correctly with the new
dependencies (references: attempts, MAX_ATTEMPTS, interval, check,
unytDnaInstalled, setUnytInstallFail).
- Around line 1367-1385: The guard that allows the <Wallet /> to mount is using
a weak check (!hostData); update the condition to require a non-empty saved host
name by checking hostData?.name (e.g., !hostData?.name || hostData.name.trim()
=== "") instead of just !hostData, and use that same stronger check in the JSX
branch that shows the Host Index Registration message so the wallet only
activates when membraneProofStatus === "done" AND hostData.name is present;
locate references to membraneProofStatus and hostData in this component (the
ternary controlling the earnings/Wallet rendering) and replace the condition
accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d5389159-7abc-487c-9d0b-07be15727ae7
📒 Files selected for processing (1)
ui/src/components/Hosting.tsx
Hosting Earnings UX Improvement
Summary
When a host enables "Paid Hosting via wHOT" before completing host index registration, they see a confusing technical error:
This PR replaces that with clear, contextual messages that guide the host through each step of the registration → earnings flow.
Problem
The wHOT/Unyt payment system requires a membrane proof issued during host registration. Without it, the Wallet component surfaces raw internal error strings that mean nothing to users and offer no guidance on how to resolve the issue.
Changes
1. Gate Earnings section on registration state and profile completeness (
Hosting.tsx)The Earnings/Wallet area now shows contextual messages based on setup progress:
regStep === "verify") → "Check your inbox or spam folder for the verification email..."<Wallet />componentThis means the Wallet component is never rendered until the membrane proof is confirmed and the host has filled in at least a name.
2. Simplify Wallet.tsx
needsRegistrationstate,isMembraneProofError()helper, andsawMembraneErrortracking3. Reorder settings sections and improve onboarding copy (
Hosting.tsx)Files Changed
ui/src/components/Hosting.tsxui/src/components/Wallet.tsxSummary by CodeRabbit
New Features
UI/UX Improvements
Bug Fixes