a7df27c754c0d75e313e7808f421d58bd46d4394
Bring in the Signal-style wallet redesign and settings refactor from
Ditto's feat/spark-wallet branch, rebranded as "Agora Wallet" throughout
the UI. The underlying SDK stays the Breez SDK Spark variant — internal
identifiers keep the 'spark' name.
Dashboard (/wallet)
- Signal-style layout: centered balance, three circular SEND / SCAN /
RECEIVE actions, flat date-grouped transaction history with
category-coloured rows.
- Tap-to-toggle between sats and USD on the balance, persisted in
localStorage (agora:wallet:balance-mode).
- Pre-flight "Wallet not available" state when VITE_BREEZ_API_KEY is
missing, so self-hosted builds without a key surface the problem
before setup.
Send dialog
- Three sub-tabs: Recipient (new useNostrRecipientSearch hook backed by
useSearchProfiles — NIP-50 + cached author fallback + debounce), Scan
(camera QR via @capacitor/barcode-scanner), Keyboard (paste / type any
LN address, invoice, or on-chain address).
- Recipient rows truncate display names at 32 chars + middle-ellipsis
the LN address so pathologically long values can't push the row past
the modal edge.
Receive dialog
- Two sub-tabs (Lightning / Bitcoin), rounded QR cards, COPY + ADD
DETAILS pills, inline sheet for amount-specific bolt11 invoice
generation. Lightning tab prefers the registered Lightning Address
over a one-shot invoice.
Settings (/settings/wallet)
- Split the monolithic WalletSettings/WalletSettingsContent into three
self-contained sections: SparkWalletSettings, WebLNSettings,
NWCSettings.
- One-tap "Use <addr> on my Nostr profile" button publishes the wallet's
Lightning Address to kind 0 lud16. Already-applied state shows a
confirmation chip.
- "Change my Lightning Address" collapses behind a toggle when the user
already has one; the claim form is primary otherwise.
- Hide-transactions-below threshold, Backup wallet sheet (relay sync /
file export / view recovery phrase / delete relay backup), Restore
existing wallet (with ?setup=restore URL deep-linking), CSV
transaction export, Remove wallet (typed-DELETE confirmation).
Onboarding
- Insert a "wallet" step into InitialSyncGate's signup flow between
Profile and Follows, with WASM-unsupported (iOS Lockdown Mode) and
feature-disabled (no API key) fallbacks plus one-tap relay backup of
the recovery phrase.
Zap integration
- useZaps tries the Agora Wallet first (when balance >= invoice amount)
before falling back to NWC -> WebLN -> manual QR.
Dead code removal
- Drop WalletLockScreen + LockTimeoutSettings components and all lock
state from SparkWalletContext (isLocked, lockWallet, unlockWallet,
lockTimeout, shouldAutoLock, updateLastActivity, LockTimeoutMinutes).
- Existing users with lockTimeout in their persisted config blob have
the field silently ignored on load (parseSparkWalletConfig does a
JSON.parse + cast, unknown fields are dropped).
Library code
- Reusable presentational primitives in
src/components/SparkWallet/primitives/: CircleAction, DateSeparator,
TransactionRow, WalletTabBar, SettingsRow.
- src/lib/spark/{exportCsv,formatDateBucket,walletAvailability}.ts
- src/hooks/useNostrRecipientSearch.ts
- docs/SPARK-WALLET.md documenting architecture, backup formats,
payment priority, onboarding placement, and the SDK upgrade-ceiling
diagnostic.
Dependencies
- @breeztech/breez-sdk-spark pinned to 0.13.1 (exact, no caret). Bumped
from the prior 0.13.2-dev1.
- @scure/bip32 ^2.0.1 and html5-qrcode ^2.3.8 added to match Ditto's
dependency set.
- One SDK breaking change handled: prepareLnurlPay's amount_sats was
renamed to amount and widened to bigint — breezService.ts:719 now
calls BigInt(amountSats).
Build / native
- vite.config.ts: worker.format 'es' + optimizeDeps exclude for
@breeztech/breez-sdk-spark (wasm loader).
- Android: CAMERA permission + uses-feature android.hardware.camera
required=false.
- iOS: broaden NSCameraUsageDescription to mention scanning Lightning
invoices / Bitcoin addresses.
- cap sync registers @capacitor/barcode-scanner for both platforms.
AppConfig
- New optional walletHideBelowSats?: number field (interface + Zod
schema) controls the per-transaction hide threshold in the wallet
history.
Help content
- "How do I connect a wallet?" FAQ leads with the built-in Agora Wallet
and its one-tap profile setup before mentioning external NWC / WebLN
options.
Settings page
- Add top-level Wallet entry, drop wallet description from Advanced
(wallet settings are no longer nested under Advanced).
Agora
Power to the people.
Agora is a Nostr client focused on community ownership, expressive identity, and censorship resistance. This repository (agora-3) is the Agora-branded app built from the Ditto codebase.
What This Repo Is
- Agora product identity (name, theme, assets, native IDs)
- Ditto-derived implementation with broad Nostr feature coverage
- Configurable deployment defaults via
agora.json
Features
- Community-first social client: notes, articles, comments, reposts, reactions, and rich event rendering
- Theming system: built-in presets + custom color/font/background themes that can be shared as events
- Lightning support: zaps with Nostr Wallet Connect and WebLN
- Private messaging: NIP-04 and NIP-17 direct messages
- Mobile app shell: Capacitor-powered Android/iOS wrappers
- Self-hostable: static web build + configurable relay and upload infrastructure
Getting Started
Prerequisites
- Node.js 22+
- npm 10.9.4+
Development
git clone https://gitlab.com/soapbox-pub/agora-3.git
cd agora-3
npm install
npm run dev
Development server: http://localhost:8080
Build
npm run build
Build output: dist/
Validate
npm test
This runs type-checking, linting, unit tests, and production build checks.
Configuration
Build-time config is read from agora.json (gitignored by default so each deployment can provide its own values).
{
"theme": "dark",
"relayMetadata": {
"relays": [
{ "url": "wss://relay.ditto.pub", "read": true, "write": true },
{ "url": "wss://relay.primal.net", "read": true, "write": true },
{ "url": "wss://relay.damus.io", "read": true, "write": true }
]
},
"blossomServers": [
"https://blossom.ditto.pub",
"https://blossom.primal.net/"
]
}
Configuration priority (highest first):
- User settings (local storage)
- Build config (
agora.json) - Hardcoded app defaults
Use a custom config path:
CONFIG_FILE=./my-config.json npm run build
Deployment
Agora builds to static files and can be deployed to any static host.
- GitLab/GitHub Pages
- Netlify/Vercel
- VPS or any web server with SPA routing fallback
For Android:
npm run build
npx cap sync
npx cap open android
Tech Stack
| Layer | Technology |
|---|---|
| Framework | React 18 |
| Build | Vite |
| Language | TypeScript |
| Styling | TailwindCSS 3 + shadcn/ui |
| Routing | React Router |
| Data | TanStack Query |
| Nostr | Nostrify + nostr-tools |
| Mobile | Capacitor |
| Testing | Vitest + React Testing Library |
Contributing
Read CONTRIBUTING.md before opening a merge request.
License
Languages
TypeScript
95.7%
Java
1.5%
JavaScript
0.9%
Swift
0.9%
CSS
0.5%
Other
0.4%