import { useEffect, useMemo, useState, useCallback } from 'react' import QRCode from 'react-qr-code' import type { AlbyInvoice } from '@/types/alby' import { getAlbyService, isWebLNAvailable } from '@/lib/alby' import { AlbyInstaller } from './AlbyInstaller' import { t } from '@/lib/i18n' interface PaymentModalProps { invoice: AlbyInvoice onClose: () => void onPaymentComplete: () => void } function useInvoiceTimer(expiresAt?: number): number | null { const [timeRemaining, setTimeRemaining] = useState(null) useEffect(() => { if (!expiresAt) { return } const updateTimeRemaining = (): void => { const now = Math.floor(Date.now() / 1000) const remaining = expiresAt - now setTimeRemaining(remaining > 0 ? remaining : 0) } updateTimeRemaining() const interval = setInterval(updateTimeRemaining, 1000) return () => clearInterval(interval) }, [expiresAt]) return timeRemaining } function PaymentHeader({ amount, timeRemaining, onClose, }: { amount: number timeRemaining: number | null onClose: () => void }): JSX.Element { const timeLabel = useMemo((): string | null => { if (timeRemaining === null) { return null } if (timeRemaining <= 0) { return t('payment.expired') } const minutes = Math.floor(timeRemaining / 60) const secs = timeRemaining % 60 return `${minutes}:${secs.toString().padStart(2, '0')}` }, [timeRemaining]) return (

{t('payment.modal.zapAmount', { amount })}

{timeLabel && (

{t('payment.modal.timeRemaining', { time: timeLabel })}

)}
) } function InvoiceDisplay({ invoiceText, paymentUrl }: { invoiceText: string; paymentUrl: string }): JSX.Element { return (

{t('payment.modal.lightningInvoice')}

{invoiceText}

{t('payment.modal.scanQr')}

) } function PaymentActions({ copied, onCopy, onOpenWallet, }: { copied: boolean onCopy: () => Promise onOpenWallet: () => void }): JSX.Element { return (
) } function ExpiredNotice({ show }: { show: boolean }): JSX.Element | null { if (!show) { return null } return (

{t('payment.modal.invoiceExpired')}

{t('payment.modal.invoiceExpiredHelp')}

) } function usePaymentModalState(invoice: AlbyInvoice, onPaymentComplete: () => void): { copied: boolean errorMessage: string | null paymentUrl: string timeRemaining: number | null handleCopy: () => Promise handleOpenWallet: () => Promise } { const [copied, setCopied] = useState(false) const [errorMessage, setErrorMessage] = useState(null) const paymentUrl = `lightning:${invoice.invoice}` const timeRemaining = useInvoiceTimer(invoice.expiresAt) const handleCopy = useCallback(async (): Promise => { try { await navigator.clipboard.writeText(invoice.invoice) setCopied(true) setTimeout(() => setCopied(false), 2000) } catch (e) { console.error('Failed to copy:', e) setErrorMessage(t('payment.modal.copyFailed')) } }, [invoice.invoice]) const handleOpenWallet = useCallback(async (): Promise => { try { const alby = getAlbyService() if (!isWebLNAvailable()) { throw new Error(t('payment.modal.weblnNotAvailable')) } await alby.enable() await alby.sendPayment(invoice.invoice) onPaymentComplete() } catch (e) { const error = e instanceof Error ? e : new Error(String(e)) if (error.message.includes('user rejected') || error.message.includes('cancelled')) { return } console.error('Payment failed:', error) setErrorMessage(error.message) } }, [invoice.invoice, onPaymentComplete]) return { copied, errorMessage, paymentUrl, timeRemaining, handleCopy, handleOpenWallet } } export function PaymentModal({ invoice, onClose, onPaymentComplete }: PaymentModalProps): JSX.Element { const { copied, errorMessage, paymentUrl, timeRemaining, handleCopy, handleOpenWallet } = usePaymentModalState(invoice, onPaymentComplete) const handleOpenWalletSync = (): void => { void handleOpenWallet() } return (
{errorMessage && (

{errorMessage}

)}

{t('payment.modal.autoVerify')}

) }