wallet: combine SP + on-chain into a single BIP-21 QR in the receive dialog

Mirrors CampaignWalletDonatePanel's payload format
(`bitcoin:<bc1>?sp=<sp1>`) so BIP-352-aware wallets pick the
`sp=` parameter and legacy wallets fall back to the on-chain
address. The dialog now shows one QR and one copyable row with the
full URI, the tabs and per-tab intro copy are gone, and the
"Scan for payments" action lives in the wallet's overflow menu
instead of inside the receive dialog.
This commit is contained in:
Alex Gleason
2026-05-24 18:19:15 -05:00
parent abd6cd95b1
commit a83df0f56f
16 changed files with 60 additions and 220 deletions
-6
View File
@@ -754,14 +754,8 @@
"receiveDialog": {
"title": "استقبال البيتكوين",
"description": "شارك عنواناً لاستقبال البيتكوين.",
"onChain": "على السلسلة",
"silentPayment": "دفع صامت",
"onChainIntro": "عنوان جديد في كل مرة. انتقل إلى فهرس جديد بعد المشاركة للحفاظ على الخصوصية.",
"addressIndex": "العنوان رقم {{index}}",
"newAddress": "عنوان جديد",
"silentIntro": "معرّف استقبال ثابت. شاركه مرة واحدة وأعد استخدامه إلى الأبد — يشتقّ المرسلون عنواناً فريداً على السلسلة لكل دفعة.",
"noIndexer": "تم تعطيل المسح — لا يوجد مفهرس BIP-352 مُهيّأ. اضبط <0>bip352IndexerUrl</0> في إعدادات التطبيق لاكتشاف المدفوعات الصامتة الواردة.",
"silentBalance": "رصيد الدفع الصامت: <0>{{amount}}</0>",
"scanForNew": "فحص المدفوعات الجديدة",
"scanForPayments": "فحص المدفوعات"
},
-6
View File
@@ -1177,14 +1177,8 @@
"receiveDialog": {
"title": "Receive Bitcoin",
"description": "Share an address to receive bitcoin.",
"onChain": "On-chain",
"silentPayment": "Silent payment",
"onChainIntro": "Fresh address each time. Bump to a new index after sharing for privacy.",
"addressIndex": "Address #{{index}}",
"newAddress": "New address",
"silentIntro": "Static receive identifier. Share once and reuse forever — senders derive a unique on-chain address per payment.",
"noIndexer": "Scanning disabled — no BIP-352 indexer configured. Set <0>bip352IndexerUrl</0> in your app config to detect incoming silent payments.",
"silentBalance": "Silent payment balance: <0>{{amount}}</0>",
"scanForNew": "Scan for new payments",
"scanForPayments": "Scan for payments"
},
-6
View File
@@ -754,14 +754,8 @@
"receiveDialog": {
"title": "Recibir Bitcoin",
"description": "Comparte una dirección para recibir bitcoin.",
"onChain": "En cadena",
"silentPayment": "Pago silencioso",
"onChainIntro": "Dirección nueva cada vez. Pasa a un nuevo índice después de compartirla para preservar tu privacidad.",
"addressIndex": "Dirección n.º {{index}}",
"newAddress": "Nueva dirección",
"silentIntro": "Identificador de recepción estático. Compártelo una vez y reutilízalo para siempre: los remitentes derivan una dirección única en cadena para cada pago.",
"noIndexer": "Escaneo desactivado: no hay indexador BIP-352 configurado. Define <0>bip352IndexerUrl</0> en la configuración de tu aplicación para detectar pagos silenciosos entrantes.",
"silentBalance": "Saldo de pago silencioso: <0>{{amount}}</0>",
"scanForNew": "Buscar nuevos pagos",
"scanForPayments": "Buscar pagos"
},
-6
View File
@@ -754,14 +754,8 @@
"receiveDialog": {
"title": "دریافت بیت‌کوین",
"description": "برای دریافت بیت‌کوین، یک نشانی به اشتراک بگذارید.",
"onChain": "روی زنجیره",
"silentPayment": "پرداخت خاموش",
"onChainIntro": "هر بار نشانی تازه. پس از اشتراک‌گذاری، برای حفظ حریم خصوصی به یک نمایه جدید بروید.",
"addressIndex": "نشانی شمارهٔ {{index}}",
"newAddress": "نشانی جدید",
"silentIntro": "شناسهٔ دریافت ایستا. یک‌بار به اشتراک بگذارید و برای همیشه استفاده کنید — فرستندگان برای هر پرداخت یک نشانی منحصربه‌فرد روی زنجیره مشتق می‌کنند.",
"noIndexer": "پویش غیرفعال است — هیچ ایندکس‌کنندهٔ BIP-352 پیکربندی نشده است. برای شناسایی پرداخت‌های خاموش ورودی، <0>bip352IndexerUrl</0> را در پیکربندی برنامه تنظیم کنید.",
"silentBalance": "موجودی پرداخت خاموش: <0>{{amount}}</0>",
"scanForNew": "پویش پرداخت‌های جدید",
"scanForPayments": "پویش پرداخت‌ها"
},
-6
View File
@@ -1177,14 +1177,8 @@
"receiveDialog": {
"title": "Recevoir du Bitcoin",
"description": "Partagez une adresse pour recevoir du bitcoin.",
"onChain": "Sur la chaîne",
"silentPayment": "Paiement silencieux",
"onChainIntro": "Nouvelle adresse à chaque fois. Passez à un nouvel index après partage pour la confidentialité.",
"addressIndex": "Adresse n°{{index}}",
"newAddress": "Nouvelle adresse",
"silentIntro": "Identifiant de réception statique. Partagez une fois et réutilisez à vie — les expéditeurs dérivent une adresse on-chain unique par paiement.",
"noIndexer": "Scan désactivé — aucun indexeur BIP-352 configuré. Définissez <0>bip352IndexerUrl</0> dans votre configuration d'application pour détecter les paiements silencieux entrants.",
"silentBalance": "Solde des paiements silencieux : <0>{{amount}}</0>",
"scanForNew": "Scanner les nouveaux paiements",
"scanForPayments": "Scanner les paiements"
},
-6
View File
@@ -1177,14 +1177,8 @@
"receiveDialog": {
"title": "Bitcoin पाएँ",
"description": "Bitcoin पाने के लिए एक एड्रेस शेयर करें।",
"onChain": "ऑन-चेन",
"silentPayment": "साइलेंट पेमेंट",
"onChainIntro": "हर बार नया एड्रेस। प्राइवेसी के लिए शेयर करने के बाद नए index पर बढ़ाएँ।",
"addressIndex": "एड्रेस #{{index}}",
"newAddress": "नया एड्रेस",
"silentIntro": "स्थिर रिसीव पहचानकर्ता। एक बार शेयर करें और हमेशा के लिए दोहराते रहें — भेजने वाले हर भुगतान के लिए एक अनूठा ऑन-चेन एड्रेस derive करते हैं।",
"noIndexer": "Scanning अक्षम है — कोई BIP-352 indexer कॉन्फ़िगर नहीं है। आने वाले साइलेंट पेमेंट का पता लगाने के लिए अपनी ऐप कॉन्फ़िग में <0>bip352IndexerUrl</0> सेट करें।",
"silentBalance": "साइलेंट पेमेंट बैलेंस: <0>{{amount}}</0>",
"scanForNew": "नए भुगतान के लिए scan करें",
"scanForPayments": "भुगतान के लिए scan करें"
},
-6
View File
@@ -1186,14 +1186,8 @@
"receiveDialog": {
"title": "Terima Bitcoin",
"description": "Bagikan alamat untuk menerima bitcoin.",
"onChain": "On-chain",
"silentPayment": "Silent payment",
"onChainIntro": "Alamat baru setiap saat. Naikkan ke indeks baru setelah berbagi untuk privasi.",
"addressIndex": "Alamat #{{index}}",
"newAddress": "Alamat baru",
"silentIntro": "Pengenal penerima statis. Bagikan sekali dan gunakan kembali selamanya — pengirim menurunkan alamat on-chain unik per pembayaran.",
"noIndexer": "Pemindaian dinonaktifkan — tidak ada indexer BIP-352 yang dikonfigurasi. Setel <0>bip352IndexerUrl</0> di konfigurasi aplikasi Anda untuk mendeteksi silent payment masuk.",
"silentBalance": "Saldo silent payment: <0>{{amount}}</0>",
"scanForNew": "Pindai pembayaran baru",
"scanForPayments": "Pindai pembayaran"
},
-6
View File
@@ -754,14 +754,8 @@
"receiveDialog": {
"title": "ទទួល Bitcoin",
"description": "ចែករំលែកអាសយដ្ឋានដើម្បីទទួល Bitcoin។",
"onChain": "លើខ្សែសង្វាក់",
"silentPayment": "ការទូទាត់ស្ងាត់",
"onChainIntro": "អាសយដ្ឋានថ្មីរាល់ពេល។ បន្ទាប់ពីចែករំលែករួច សូមផ្លាស់ទៅសន្ទស្សន៍ថ្មីដើម្បីការពារភាពឯកជន។",
"addressIndex": "អាសយដ្ឋាន #{{index}}",
"newAddress": "អាសយដ្ឋានថ្មី",
"silentIntro": "អត្តសញ្ញាណទទួលនៅស្ងៀម។ ចែករំលែកម្ដងហើយប្រើឡើងវិញជារៀងរហូត — អ្នកផ្ញើបង្កើតអាសយដ្ឋានពិសេសលើខ្សែសង្វាក់សម្រាប់ការទូទាត់នីមួយៗ។",
"noIndexer": "ការស្កេនត្រូវបានបិទ — គ្មានអ្នកធ្វើលិបិក្រម BIP-352 បានកំណត់ទេ។ កំណត់ <0>bip352IndexerUrl</0> ក្នុងការកំណត់កម្មវិធីរបស់អ្នកដើម្បីរកការទូទាត់ស្ងាត់ដែលចូលមក។",
"silentBalance": "សមតុល្យការទូទាត់ស្ងាត់៖ <0>{{amount}}</0>",
"scanForNew": "ស្កេនការទូទាត់ថ្មី",
"scanForPayments": "ស្កេនការទូទាត់"
},
-6
View File
@@ -754,14 +754,8 @@
"receiveDialog": {
"title": "د بټکوین ترلاسه کول",
"description": "د بټکوین د ترلاسه کولو لپاره یوه پته شریکه کړئ.",
"onChain": "په زنځیر کې",
"silentPayment": "چوپه ورکړه",
"onChainIntro": "هر ځل نوې پته. د شریکولو وروسته د محرمیت لپاره یوې نوې شمارلیکې ته لاړ شئ.",
"addressIndex": "پته #{{index}}",
"newAddress": "نوې پته",
"silentIntro": "د ترلاسه کولو ثابته پېژند. یوځل شریکه کړئ او د تل لپاره بیا یې کاروئ — لیږونکي د هرې ورکړې لپاره په زنځیر کې یوه ځانګړې پته راپیدا کوي.",
"noIndexer": "سکن غیرفعال دی — د BIP-352 ایندکس کوونکی نه دی تنظیم شوی. د راتلونکو چوپو ورکړو د کشف لپاره ستاسو د اپلیکیشن تنظیماتو کې <0>bip352IndexerUrl</0> وټاکئ.",
"silentBalance": "د چوپې ورکړې بیلانس: <0>{{amount}}</0>",
"scanForNew": "د نوو ورکړو لپاره سکن",
"scanForPayments": "د ورکړو لپاره سکن"
},
-6
View File
@@ -1177,14 +1177,8 @@
"receiveDialog": {
"title": "Receber Bitcoin",
"description": "Compartilhe um endereço para receber bitcoin.",
"onChain": "On-chain",
"silentPayment": "Pagamento silencioso",
"onChainIntro": "Endereço novo a cada vez. Avance para um novo índice após compartilhar para privacidade.",
"addressIndex": "Endereço n.º {{index}}",
"newAddress": "Novo endereço",
"silentIntro": "Identificador de recebimento estático. Compartilhe uma vez e reutilize para sempre — remetentes derivam um endereço on-chain único por pagamento.",
"noIndexer": "Escaneamento desativado — nenhum indexador BIP-352 configurado. Defina <0>bip352IndexerUrl</0> na sua configuração do app para detectar pagamentos silenciosos recebidos.",
"silentBalance": "Saldo de pagamentos silenciosos: <0>{{amount}}</0>",
"scanForNew": "Escanear novos pagamentos",
"scanForPayments": "Escanear pagamentos"
},
-6
View File
@@ -1177,14 +1177,8 @@
"receiveDialog": {
"title": "Получить Bitcoin",
"description": "Поделитесь адресом для получения bitcoin.",
"onChain": "В блокчейне",
"silentPayment": "Тихий платёж",
"onChainIntro": "Свежий адрес каждый раз. Переходите на новый индекс после публикации для конфиденциальности.",
"addressIndex": "Адрес №{{index}}",
"newAddress": "Новый адрес",
"silentIntro": "Статический идентификатор получения. Поделитесь один раз и используйте навсегда — отправители получают уникальный адрес в блокчейне на каждый платёж.",
"noIndexer": "Сканирование отключено — индексер BIP-352 не настроен. Установите <0>bip352IndexerUrl</0> в конфигурации вашего приложения, чтобы обнаруживать входящие тихие платежи.",
"silentBalance": "Баланс тихих платежей: <0>{{amount}}</0>",
"scanForNew": "Сканировать новые платежи",
"scanForPayments": "Сканировать платежи"
},
-6
View File
@@ -754,14 +754,8 @@
"receiveDialog": {
"title": "Gamuchira Bitcoin",
"description": "Govera kero yokugamuchira bitcoin.",
"onChain": "PaCheni",
"silentPayment": "Mubhadharo wakanyararira",
"onChainIntro": "Kero itsva nguva dzese. Chinjira kune index itsva mushure mokugovera kuti uchengetedze privacy.",
"addressIndex": "Kero #{{index}}",
"newAddress": "Kero itsva",
"silentIntro": "Chiratidzo chokugamuchira chakamira. Govera kamwe woshandisazve nokusingaperi — vatumiri vanobvisa kero yakasiyana pacheni pamubhadharo woga woga.",
"noIndexer": "Kuongorora kwadzimwa — hapana muongorori weBIP-352 wakaiswa. Isa <0>bip352IndexerUrl</0> mukurongedzwa kweapp yako kuti uone mibhadharo yakanyararira inopinda.",
"silentBalance": "Bharanzi remubhadharo wakanyararira: <0>{{amount}}</0>",
"scanForNew": "Tsvaga mibhadharo mitsva",
"scanForPayments": "Tsvaga mibhadharo"
},
-6
View File
@@ -1186,14 +1186,8 @@
"receiveDialog": {
"title": "Bitcoin Al",
"description": "Bitcoin almak için bir adres paylaşın.",
"onChain": "Zincir üstü",
"silentPayment": "Sessiz ödeme",
"onChainIntro": "Her seferinde yeni adres. Gizlilik için paylaşımdan sonra yeni bir dizine geçin.",
"addressIndex": "Adres #{{index}}",
"newAddress": "Yeni adres",
"silentIntro": "Sabit alım tanımlayıcısı. Bir kez paylaşın ve sonsuza dek tekrar kullanın — göndericiler her ödeme için benzersiz bir zincir üstü adres türetir.",
"noIndexer": "Tarama devre dışı — BIP-352 indeksleyici yapılandırılmamış. Gelen sessiz ödemeleri algılamak için uygulama yapılandırmanızda <0>bip352IndexerUrl</0> ayarını yapın.",
"silentBalance": "Sessiz ödeme bakiyesi: <0>{{amount}}</0>",
"scanForNew": "Yeni ödemeler için tara",
"scanForPayments": "Ödemeler için tara"
},
-6
View File
@@ -754,14 +754,8 @@
"receiveDialog": {
"title": "接收比特幣",
"description": "分享一個地址以接收比特幣。",
"onChain": "鏈上",
"silentPayment": "靜默支付",
"onChainIntro": "每次都生成新地址。分享後請切換到新索引以保護隱私。",
"addressIndex": "地址 #{{index}}",
"newAddress": "新地址",
"silentIntro": "靜態接收識別符號。一次分享,永久複用——傳送方為每筆支付派生唯一的鏈上地址。",
"noIndexer": "掃描已停用——未配置 BIP-352 索引器。在應用配置中設定 <0>bip352IndexerUrl</0> 以檢測傳入的靜默支付。",
"silentBalance": "靜默支付餘額:<0>{{amount}}</0>",
"scanForNew": "掃描新支付",
"scanForPayments": "掃描支付"
},
-6
View File
@@ -754,14 +754,8 @@
"receiveDialog": {
"title": "接收比特币",
"description": "分享一个地址以接收比特币。",
"onChain": "链上",
"silentPayment": "静默支付",
"onChainIntro": "每次都生成新地址。分享后请切换到新索引以保护隐私。",
"addressIndex": "地址 #{{index}}",
"newAddress": "新地址",
"silentIntro": "静态接收标识符。一次分享,永久复用——发送方为每笔支付派生唯一的链上地址。",
"noIndexer": "扫描已禁用——未配置 BIP-352 索引器。在应用配置中设置 <0>bip352IndexerUrl</0> 以检测传入的静默支付。",
"silentBalance": "静默支付余额:<0>{{amount}}</0>",
"scanForNew": "扫描新支付",
"scanForPayments": "扫描支付"
},
+60 -130
View File
@@ -1,7 +1,7 @@
import { useRef, useState } from 'react';
import { Link } from 'react-router-dom';
import { useSeoMeta } from '@unhead/react';
import { useTranslation, Trans } from 'react-i18next';
import { useTranslation } from 'react-i18next';
import {
Copy,
Check,
@@ -34,7 +34,6 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { HDSendBitcoinDialog } from '@/components/HDSendBitcoinDialog';
import { HDSilentPaymentScanDialog } from '@/components/HDSilentPaymentScanDialog';
import { WalletBackupMnemonicDialog } from '@/components/WalletBackupMnemonic';
@@ -64,8 +63,7 @@ export function WalletPage() {
const sp = useHdWalletSp();
const { data: btcPrice } = useHdBtcPrice();
const [copiedAddress, setCopiedAddress] = useState(false);
const [copiedSp, setCopiedSp] = useState(false);
const [copiedPayload, setCopiedPayload] = useState(false);
const [txOpen, setTxOpen] = useState(false);
const [sendOpen, setSendOpen] = useState(false);
const [receiveOpen, setReceiveOpen] = useState(false);
@@ -80,36 +78,30 @@ export function WalletPage() {
const address = currentReceiveAddress?.address ?? '';
const spAddress = silentPaymentAddress?.address ?? '';
const copyAddress = async () => {
if (!address) return;
// Combined BIP-21 payload: `bitcoin:<bc1>?sp=<sp1>` when both are
// available, falling back to the single endpoint that exists.
// Mirrors the campaign donate panel's QR payload so BIP-352-aware
// wallets pick the `sp=` parameter and legacy wallets fall back to
// the on-chain address.
const qrPayload = address && spAddress
? `bitcoin:${address}?sp=${spAddress}`
: address
? `bitcoin:${address}`
: spAddress
? `bitcoin:?sp=${spAddress}`
: '';
const copyPayload = async () => {
if (!qrPayload) return;
try {
await navigator.clipboard.writeText(address);
setCopiedAddress(true);
setTimeout(() => setCopiedAddress(false), 2000);
await navigator.clipboard.writeText(qrPayload);
setCopiedPayload(true);
setTimeout(() => setCopiedPayload(false), 2000);
} catch {
// clipboard unavailable
}
};
const copySpAddress = async () => {
if (!spAddress) return;
try {
await navigator.clipboard.writeText(spAddress);
setCopiedSp(true);
setTimeout(() => setCopiedSp(false), 2000);
} catch {
// clipboard unavailable
}
};
const truncatedAddress = address
? `${address.slice(0, 12)}...${address.slice(-8)}`
: '';
const truncatedSpAddress = spAddress
? `${spAddress.slice(0, 12)}...${spAddress.slice(-8)}`
: '';
// ── Logged out ────────────────────────────────────────────────
if (availability.status === 'logged-out') {
return (
@@ -185,6 +177,14 @@ export function WalletPage() {
<KeyRound className="size-4 mr-2" />
{t('walletSettings.backup.label')}
</DropdownMenuItem>
{sp.enabled && spAddress && (
<DropdownMenuItem onSelect={() => setSpScanOpen(true)} className="cursor-pointer">
<Radar className="size-4 mr-2" />
{sp.storage?.scanHeight && sp.storage.scanHeight > 0
? t('wallet.receiveDialog.scanForNew')
: t('wallet.receiveDialog.scanForPayments')}
</DropdownMenuItem>
)}
<DropdownMenuItem asChild>
<Link to="/wallet/legacy" className="cursor-pointer">
<History className="size-4 mr-2" />
@@ -291,115 +291,45 @@ export function WalletPage() {
</DialogDescription>
</DialogHeader>
<Tabs defaultValue="onchain" className="w-full">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="onchain">{t('wallet.receiveDialog.onChain')}</TabsTrigger>
<TabsTrigger value="silent" disabled={!spAddress}>
{t('wallet.receiveDialog.silentPayment')}
</TabsTrigger>
</TabsList>
{qrPayload && (
<div className="flex flex-col items-center gap-4">
{/* Combined BIP-21 QR. BIP-352-aware wallets pick the
`sp=` parameter; legacy wallets fall back to the
on-chain address. Mirrors CampaignWalletDonatePanel. */}
<div className="rounded-2xl bg-white p-4 shadow-sm">
<QRCodeCanvas value={qrPayload} size={220} level="M" />
</div>
{/* Copyable row showing the full payment URI. */}
<button
type="button"
onClick={copyPayload}
className="w-full flex items-center gap-2 rounded-lg border bg-muted/40 px-3 py-2.5 text-left hover:bg-muted/60 motion-safe:transition-colors cursor-pointer"
>
<span className="flex-1 min-w-0 truncate font-mono text-xs" title={qrPayload}>
{qrPayload}
</span>
{copiedPayload ? (
<Check className="size-4 text-green-500 shrink-0" />
) : (
<Copy className="size-4 text-muted-foreground shrink-0" />
)}
</button>
{/* ── On-chain (BIP86 single-use) ──────────────── */}
<TabsContent value="onchain" className="mt-4">
{address && (
<div className="flex flex-col items-center gap-4">
<p className="text-xs text-muted-foreground text-center max-w-xs">
{t('wallet.receiveDialog.onChainIntro')}
</p>
<div className="rounded-2xl bg-white p-4 shadow-sm">
<QRCodeCanvas value={address} size={200} level="M" />
</div>
<div className="flex items-center gap-3 text-xs text-muted-foreground">
<span>{t('wallet.receiveDialog.addressIndex', { index: currentReceiveAddress?.index ?? 0 })}</span>
<span aria-hidden>·</span>
<button
onClick={copyAddress}
className="flex items-center gap-2 rounded-full border px-4 py-2 text-sm font-mono text-muted-foreground hover:bg-muted/50 transition-colors cursor-pointer"
onClick={() => nextReceiveAddress()}
className="hover:text-foreground underline-offset-4 hover:underline transition-colors cursor-pointer"
>
{truncatedAddress}
{copiedAddress ? (
<Check className="size-3.5 text-green-500" />
) : (
<Copy className="size-3.5" />
)}
{t('wallet.receiveDialog.newAddress')}
</button>
<div className="flex items-center gap-3 text-xs text-muted-foreground">
<span>{t('wallet.receiveDialog.addressIndex', { index: currentReceiveAddress?.index ?? 0 })}</span>
<span aria-hidden>·</span>
<button
onClick={() => nextReceiveAddress()}
className="hover:text-foreground underline-offset-4 hover:underline transition-colors cursor-pointer"
>
{t('wallet.receiveDialog.newAddress')}
</button>
</div>
</div>
)}
</TabsContent>
{/* ── Silent payment (BIP-352 static) ──────────── */}
<TabsContent value="silent" className="mt-4">
{spAddress && (
<div className="flex flex-col items-center gap-4">
<p className="text-xs text-muted-foreground text-center max-w-xs">
{t('wallet.receiveDialog.silentIntro')}
</p>
<div className="rounded-2xl bg-white p-4 shadow-sm">
<QRCodeCanvas value={spAddress} size={220} level="L" />
</div>
<button
onClick={copySpAddress}
className="flex items-center gap-2 rounded-full border px-4 py-2 text-sm font-mono text-muted-foreground hover:bg-muted/50 transition-colors cursor-pointer"
>
{truncatedSpAddress}
{copiedSp ? (
<Check className="size-3.5 text-green-500" />
) : (
<Copy className="size-3.5" />
)}
</button>
{sp.unavailableReason === 'no-indexer' ? (
<p className="text-xs text-orange-500 dark:text-orange-400 text-center max-w-xs">
<Trans
i18nKey="wallet.receiveDialog.noIndexer"
components={{ 0: <span className="font-mono" /> }}
/>
</p>
) : sp.enabled ? (
<div className="flex flex-col items-center gap-2 w-full">
{sp.balance > 0 && (
<p className="text-xs text-muted-foreground text-center">
<Trans
i18nKey="wallet.receiveDialog.silentBalance"
values={{
amount: btcPrice
? satsToUSD(sp.balance, btcPrice)
: `${formatBTC(sp.balance)} BTC`,
}}
components={{ 0: <span className="text-foreground font-medium" /> }}
/>
</p>
)}
<Button
variant="outline"
size="sm"
onClick={() => setSpScanOpen(true)}
className="rounded-full"
>
<Radar className="size-3.5 mr-1.5" />
{sp.storage?.scanHeight && sp.storage.scanHeight > 0
? t('wallet.receiveDialog.scanForNew')
: t('wallet.receiveDialog.scanForPayments')}
</Button>
</div>
) : null}
</div>
)}
</TabsContent>
</Tabs>
</div>
)}
</DialogContent>
</Dialog>