Files
client/plans/wallet-page.md
Laan Tungir 073fb9e9af Feed upgrade Phase 1 & 2: simplified feed2.html + post-feed.html thread page
Phase 1 (feed2.html):
- Removed inline comment rendering and Comments toggle button
- Comment button now opens post-feed.html?event=<eventId> in new tab
- Removed inline reply composer (kept quote composer)
- Fixed missing queryCache dependency in initPostCards
- Fixed hardcoded color:red -> var(--primary-color)

Phase 2 (post-feed.html):
- New post thread page showing post + all replies + inline composer
- Parses ?event=<hex|nevent|note> URL parameter
- Fetches replies via { kinds: [1], '#e': [postId] }
- Real-time subscriptions for new replies
- Comment button on replies opens sub-threads in new tabs
- Error states for missing/invalid/not-found posts
- All CSS uses variables, no hardcoded colors
2026-06-25 19:45:36 -04:00

8.4 KiB
Raw Blame History

Cashu Wallet Enhancements — Implementation Plan

Overview

Enhance the existing www/cashu.html page and zap flow, centered on Cashu as the primary (and only) payment rail. The core functionality is already implemented — this plan adds UI improvements to show zap capabilities in the feed and let users choose their zap rail.

Design philosophy: Cashu-only

The project uses Cashu for everything — no NWC, CLINK, onchain, or Lightning channels to maintain:

Payment type How it works Already implemented?
Nutzap (NIP-61) Send ecash proofs locked to recipient's p2pk via kind 9321 Yes — walletSendNutzap
Lightning zap (NIP-57) Melt Cashu ecash at a mint to pay a BOLT11 invoice Yes — walletPayInvoice
P2P ecash transfer Send cashuB token directly to another user Yes — walletSendToken
Lightning deposit Pay a BOLT11 invoice, mint issues ecash proofs Yes — walletCreateDeposit
Lightning withdrawal Melt ecash to pay an external BOLT11 invoice Yes — walletPayInvoice

Phase 1: NIP-60 Compliance Fix (DONE )

Already completed and deployed (v0.7.19v0.7.24):

  • Fixed kind 17375 content format from proprietary {mints, nutzap} object to NIP-60 tag-array [["mint",url],["privkey",hex]]
  • Removed non-standard pubkey tag from 17375 public tags
  • Added parseWalletContent() backwards-compat shim for reading both formats
  • Added auto-migration for legacy 17375 events on startup
  • Added "Republish Wallet (kind 17375)" button with lightweight 17375-only republish
  • Added ensureDirectNutzapP2pk() call in publishDirectProofs so privkey is always included

Phase 2: Zap Capability Indicators in the Feed

Show zap capability badges on each post's interaction bar so users can see at a glance whether the recipient can receive nutzaps, Lightning zaps, or both.

Current state

The interaction bar in www/js/post-interactions.mjs already renders:

  • Zap button with count
  • 🥜 Nutzap count display (separate element showing total nutzaps received)

What to add

Small capability icons/badges next to the zap button showing what the recipient can receive:

┌──────────────────────────────────────────────────┐
│  ⚡ 5  🥜 2  🥜✅  ⚡✅     💬 3  🔄 1    2h ago │
│  └─ zap ─┘  └─ nutzap ─┘  └─ capability badges ─┘ │
└──────────────────────────────────────────────────┘

Or a more compact design — color the zap icon differently based on capabilities:

Visual Meaning
(default color) Recipient can receive Lightning zaps (has lud16)
+ 🥜 (nut badge) Recipient can also receive nutzaps (has kind 10019 with shared mint)
(dimmed) Recipient cannot receive any zaps (no lud16, no 10019)

Implementation

  • 2.1 Create a resolveZapCapabilities(pubkey) function that checks:

    • Does the recipient have a lud16 (Lightning address)? → can receive Lightning zaps
    • Does the recipient have a kind 10019 (nutzap mint list)? → can receive nutzaps
    • If yes to 10019, which mints? Do any overlap with the sender's wallet mints?
  • 2.2 Cache the results per pubkey (the kind 10019 and lud16 don't change often). Use a simple in-memory Map with TTL.

  • 2.3 Update createInteractionItem for the zap type to show capability indicators:

    • Add a small 🥜 badge on the zap button if the recipient can receive nutzaps
    • Add a tooltip showing the shared mints
    • Dim the zap button if the recipient can't receive any zaps
  • 2.4 Fetch kind 10019 events for post authors as the feed loads. The worker already fetches kind 10019 for nutzap sending — extend this to cache the results for feed display. Use the existing walletFetchMintList worker function.

  • 2.5 Show shared mint info on hover/tooltip: "Can nutzap via mint.minibits.cash/Bitcoin" or "No shared mints — Lightning only" or "No zap capability"

Where the code lives

File What to change
www/js/post-interactions.mjs Add capability badges to zap button rendering
www/js/zaps.mjs Add resolveZapCapabilities() function (or extend existing resolveZapSpecForPubkey)
www/ndk-worker.js May need a lightweight "fetch 10019 for display" message (or reuse walletFetchMintList)

Phase 3: Zap Rail Selector in the Zap Dialog

When the user taps the zap button, show which rails are available and let them choose.

flowchart TD
    A[User taps zap button] --> B[Show zap dialog with amount input]
    B --> C{What rails are available?}
    C -->|Nutzap + Lightning| D[Show rail toggle: 🥜 Nutzap | ⚡ Lightning]
    C -->|Lightning only| E[Show Lightning only]
    C -->|Nutzap only| F[Show Nutzap only]
    C -->|Neither| G[Show error: recipient cannot receive zaps]
    D --> H[User selects rail + amount]
    H --> I{Rail selected}
    I -->|Nutzap| J[Send NIP-61 nutzap via walletSendNutzap]
    I -->|Lightning| K[Create LN invoice, melt Cashu via walletPayInvoice]
    E --> K
    F --> J
  • 3.1 Update promptZapDetails in zaps.mjs to accept and display rail options:

    • If nutzap is available (recipient has 10019 with shared mint) → show "🥜 Nutzap" option
    • If Lightning is available (recipient has lud16) → show " Lightning" option
    • Let the user pick which rail to use via a toggle/selector
  • 3.2 Show balance info in the dialog:

    • "Cashu balance: 261 sats"
    • If amount > balance → show warning "Insufficient balance"
  • 3.3 Default rail selection:

    • If nutzap is available and amount < 1000 sats → default to nutzap (lower fees)
    • If nutzap is available and amount ≥ 1000 sats → default to Lightning (more reliable for larger amounts)
    • If nutzap is not available → default to Lightning
  • 3.4 Pass the selected rail back to handleZapClick in post-interactions.mjs so it uses the right send function.


Phase 4: cashu.html UI Polish

Minor UI improvements to the existing cashu.html page:

  • 4.1 Consider renaming the page title from "CASHU" to "WALLET" since it's the project's unified wallet.

  • 4.2 Update the sidenav entry in index.html from "CASHU" to "WALLET" (keeping the same cashu.html URL).

  • 4.3 Add a "Zap via Lightning" info note in the Withdraw panel explaining that this melts ecash to pay a Lightning invoice — same mechanism used for Lightning zaps.


NIP Compliance Reference

Rail NIP Kind(s) Status
Cashu Wallet NIP-60 17375, 7375, 7376, 375 Fixed and working
Cashu Nutzap NIP-61 10019, 9321 Working
Lightning Zap NIP-57 9734, 9735 Working (via Cashu melt)

Key Files

Files to modify

File Changes
www/js/post-interactions.mjs Add zap capability badges to interaction bar, pass selected rail to zap handler
www/js/zaps.mjs Add resolveZapCapabilities(), update promptZapDetails with rail selector
www/cashu.html Minor UI polish (title, info notes)
www/index.html Sidenav label: "CASHU" → "WALLET"

Files reused as-is

File Why no changes needed
www/js/cashu-wallet.mjs Controller already wraps all worker functions
www/ndk-worker.js All wallet functions already exist (payInvoice, sendNutzap, fetchMintList)
www/js/init-ndk.mjs All wallet exports already exist

Implementation Order

  1. Phase 1 (DONE) — NIP-60 compliance fix
  2. Phase 2 (zap capability indicators) — show what the recipient can receive in the feed
  3. Phase 3 (rail selector) — let users choose nutzap vs Lightning melt in the zap dialog
  4. Phase 4 (UI polish) — minor label/info changes

No new files needed. No new infrastructure. Just UI improvements on top of the existing working zap flow.