web: subscription UI#407
Open
Jared-dz wants to merge 12 commits into
Open
Conversation
|
🔗 Preview: https://pr-407.data.malbeclabs.com |
Run `doublezero latency --json` on the subscribing server, paste the output into Step 1 to get a ranked device table with Recommended/Next best badges and latency column. IP input and subscription flow unchanged for users without the CLI.
- Read VITE_SOLANA_RPC_URL from env (falls back to mainnet-beta) - Surface Program log lines in simulation errors instead of generic Anchor codes - Fix latency command display and decimal precision
Contributor
|
I went to take a look at this, but it's based off a pretty stale main. Can you rebase it? |
Replace the single-screen subscribe form with a map landing (per-metro pins via maplibre) that drops into a 4-step linear wizard (Device, Server IP, Fund, Confirm) once a device is picked. URL drives the stage: ?device= opens the wizard, ?metro= pre-opens the drawer. Surfaces two checkout lanes — Guest (functional) and Login (placeholder). Sign-in itself works via existing useAuth/UserPopover; Login-exclusive features (auto-renew, email receipt, card/invoice payment, subscription history, multi-seat) render as faded DisabledFeatureCards with either 'Backend not yet supported' or 'Coming with Login flow' disclaimers. On-chain subscribe path, transaction hooks, simulate mode, and the existing /dz/devices?device= deep link are preserved verbatim.
# Conflicts: # web/src/App.tsx # web/src/components/sidebar.tsx
Lazy-loads a minimal three.js globe (via react-globe.gl) for the shreds picker landing, with a WebGL availability check that falls back to the existing 2D MapLibre map. Keeps three.js out of the main bundle for list-view users and unsupported browsers.
Search input on the map/list toggle row lets users type a metro code or name and pick from a ranked dropdown (seats-free desc) instead of hunting on the globe. Keyboard navigable (arrows / Enter / Esc), selection opens the existing per-metro devices drawer.
Adds a Subscribe | Withdraw tab control to /dz/shreds/pay (URL-driven via ?mode=withdraw). The withdraw flow mirrors the subscribe wizard's step layout: pick a seat from the connected wallet's funded seats, then confirm with a tenure-loss warning and refund summary before signing. Reuses buildUnsubscribeInstructions and the seat/escrow on-chain state hook; no backend or program changes. Section and StepBar are extracted to wizard-shared so both wizards share the same chrome. Includes an internal-users-only preview mode (?preview=true) that injects sample seats and a mocked tx state machine so the workflow can be demoed without a wallet that holds real seats. Also drops the unimplemented "Auto-renew" placeholder card from the subscribe wizard.
Adds /account/subscriptions, a logged-in user view of their shred seats with stat strip, filter toolbar, bulk-select bar, detail drawer (overview / IPs / activity / receipts / CLI tabs), and deposit/withdraw flows. Reuses fetchShredClientSeats with funder filter; the drawer's activity tab uses the seat: filter on escrow events. Adds a preview mode for internal users (?preview=true gated on is_internal_user) that swaps in eight fixture seats covering active / low / pending / expired, fake escrow events per seat, and a shared useMockedShredTransaction hook so deposit/withdraw/bulk-deposit modals run the full sign-send-confirm sequence with no wallet calls. The withdraw wizard's local mocked hook is replaced by the shared one. The shred-fund-modal also gains preset amount chips (1 / 4 / 15 / 90 epochs). My Subscriptions is reachable from the user popover and from the Manage tab on /dz/shreds/pay.
The lane banner now shows a clickable Sign in button when signed out, and is rendered on the Withdraw mode in addition to Subscribe. A third Manage tab appears beside Subscribe / Withdraw when the user is signed in and links to /account/subscriptions. Also fixes a rules-of-hooks crash where useState(showLoginModal) was called after the pricingLoading / pricingError early returns, causing the page to error out on first load.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary of Changes
Builds the self-serve shreds subscribe flow on top of the basic 3-step form on
main. End-state at/dz/shreds/pay:fetchMetroslat/lng. Clicking a pin opens a sticky right drawer listing the devices in that metro with[Select →]buttons;?metro=<code>mirrors drawer state in the URL.?device=<code>is set. Sticky receipt panel on the right with live total and a Pay button that mirrors the Step 4 Subscribe handler — single transaction path.amount = N × price); custom amounts still work. Live epoch counter and end-of-epoch warning preserved.doublezero latency --jsonoutput (with copy command and target-server guidance) ranks devices, badges the top match⚡, and surfaces it on the map pin and in the drawer rows. Available in both Map and List views.useAuth()/UserPopover; Login-exclusive features (auto-renew, email receipt, card/invoice payment, account-tied subscription history, multi-seat / CSV IPs) render as fadedDisabledFeatureCardstubs labeled either "Backend not yet supported" or "Coming with Login flow." Lane banner switches copy based onisAuthenticated; receipt panel header reflects the lane.?simulate=true— amber banner, simulate button, "Simulation passed" state. Simulation error display now surfaces Program log lines instead of generic Anchor codes./dz/devicesrow click still lands directly in the wizard with Step 1 pre-completed.VITE_SOLANA_RPC_URL(falls back to mainnet-beta).Net effect: a buyer can land on the page cold, pick a region geographically (or paste latency JSON for precision), see exactly what they'll pay, and submit a USDC subscribe transaction — all without leaving the page. The on-chain instruction set is unchanged.
Diff Breakdown
UI-only against the existing on-chain subscribe path; no Go handlers, no API, no on-chain instruction changes.
Key files (click to expand)
web/src/components/shreds-subscribe-page.tsx(+615 / -426) — page evolves across the branch from a 3-step form into a thin orchestrator that owns URL state, data fetching, and transaction handlers, then routes to eitherMapLandingorWizardbased on?device=.web/src/components/shreds/wizard.tsx(+530, new) — 4-step wizard: step pills, device summary with "Change device", IP input, epoch presets, USDC amount input + balance, on-chain seat-exists notice, epoch progress, disabled stubs (multi-seat, CSV, auto-renew, card/invoice, email receipt), and the wallet-connect / Subscribe / TransactionProgress block.web/src/components/shreds/map-landing.tsx(+503, new) — maplibre map joined tofetchMetros, per-metro pin colour by seat availability, sticky drawer with device rows, Map/List toggle,doublezero latency --jsonpaste expander inside the drawer.web/src/components/shreds/device-picker.tsx(+140, new) — searchable device table with latency column + Recommended / Next-best badges; powers the Map landing's List toggle.web/src/components/shreds/receipt-panel.tsx(+122, new) — sticky lane-aware receipt; Pay button shares the wizard's Subscribe handler so both buttons trigger the same on-chain path.web/src/hooks/use-shred-transaction.ts(+55 / -1) — adds thesimulatepath used by?simulate=true, surfaces Program log lines in error state.web/src/components/shreds/transaction-progress.tsx(+57, new) —TransactionProgress+StatusStep(extracted, verbatim).web/src/components/shreds/epoch-progress.tsx(+55, new) —EpochProgress+formatEta+EpochWarning(extracted, verbatim).web/src/hooks/use-epoch-info.ts(+38, new) — polls slot progress + remaining time; powers the live epoch counter.web/src/components/shreds/disabled-feature-card.tsx(+25, new) — reusable disclaimer wrapper (opacity-60, dashed border,AlertCircle, reason copy).web/src/components/shreds/types.ts(+19, new) — sharedLatencyEntry/DeviceLatencyshapes.Testing Verification
/dz/shreds/pay— map renders with per-metro pins, orange where seats are free, grey where full; legend visible;UserPopovershows "Sign in" while logged out; lane banner reads guest copy.Select →buttons; URL gains?metro=<code>; back button closes the drawer.doublezero latency --jsonarray → top-ranked reachable device floats to the top of the drawer and gets the ⚡ badge on its metro pin.Recommended/Next bestbadges (when latency JSON pasted); selecting a row navigates to the wizard with?device=<code>./dz/shreds/pay?device=<code>directly (the existing/dz/devicesdeep link) → lands straight in the wizard with Step 1 marked done.[4]→ amount field auto-populates to4 × price; receipt panel total updates live. Custom-typed amounts also work; below-min and insufficient-balance error states still surface; live epoch counter + end-of-epoch warning render.DisabledFeatureCardis non-interactive and shows the correct copy variant ("Backend not yet supported" vs "Coming with Login flow").TransactionProgressadvances signing → sending → confirming → confirmed with the Solscan link.?simulate=truepreserves the amber banner, Simulate button, and "Simulation passed" state. A failing simulation surfaces the Program log lines from the on-chain trace.UserPopover→ lane banner switches to "Signed in as<email>"; receipt panel header switches from "Guest checkout" badge to the email; Login-exclusive stubs now read "Coming with Login flow." Log out → returns to guest state.