SkateSpots/src/hooks/wallet/useLightningOperations.ts
shakespeare.diy e29d35f1e4 fix: restore full skate-spots source tree from misplaced src/src/ copy
The entire skate-spots codebase was accidentally copied into src/src/ and
public/public/ instead of src/ and public/ directly. This left the real
src/ directory as blank MKStack template stubs, explaining why the landing
page showed the default template instead of SkateSpots.

Changes made:
- Copied all files from src/src/ up to src/ (App.tsx, AppRouter.tsx, main.tsx,
  all pages, components, hooks, contexts, lib, i18n, test)
- Copied all files from public/public/ up to public/ (images, manifest, etc.)
- Deleted src/src/ and public/public/ nested directories
- Removed HelloWorld.tsx template stub from pages/
- Retained the previously fixed index.css (Tailwind v3 directives)

Full skate-spots feature set is now active:
- All 30+ routes (map, events, spots, coaches, marketplace, wallet, etc.)
- NWCProvider, LanguageProvider, PWAInstallPrompt, AmberCallbackHandler
- Skate community relays (goskatespots.nostr1.com, wotr.relatr.xyz, etc.)
- Outfit Variable font, Amber pre-check in main.tsx
- All hooks (useSkateSpots, useCoaches, useZaps, etc.)
- i18n support (en/es)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-15 22:18:28 +07:00

156 lines
4.3 KiB
TypeScript

import { useState, useCallback, useRef } from 'react';
import type { Manager } from 'coco-cashu-core';
import { useToast } from '@/hooks/useToast';
interface UseLightningOperationsProps {
coco: Manager | null;
activeMintUrl: string | null;
generateQRCode: (text: string) => Promise<string>;
}
export function useLightningOperations({
coco,
activeMintUrl,
generateQRCode,
}: UseLightningOperationsProps) {
const { toast } = useToast();
// Receive state
const [quoteAmount, setQuoteAmount] = useState('');
const [showInvoiceModal, setShowInvoiceModal] = useState(false);
const [currentInvoice, setCurrentInvoice] = useState<{
quoteId: string;
mintUrl: string;
amount: number;
invoice: string;
qrCodeUrl: string;
expiry: number;
} | null>(null);
// Send state
const [lightningInvoice, setLightningInvoice] = useState('');
const [isProcessingPayment, setIsProcessingPayment] = useState(false);
const mintQuoteUnsubscribeRef = useRef<(() => void) | null>(null);
// Handle receive (create mint quote/invoice)
const handleLightningReceive = useCallback(async () => {
if (!coco || !quoteAmount || !activeMintUrl) return;
try {
const amount = parseInt(quoteAmount);
if (isNaN(amount) || amount <= 0) {
toast({
variant: 'destructive',
title: 'Invalid amount',
description: 'Please enter a valid amount'
});
return;
}
const quote = await coco.quotes.createMintQuote(activeMintUrl, amount);
const qrCodeUrl = await generateQRCode(quote.request);
setCurrentInvoice({
quoteId: quote.quote,
mintUrl: activeMintUrl,
amount,
invoice: quote.request,
qrCodeUrl,
expiry: quote.expiry,
});
setShowInvoiceModal(true);
setQuoteAmount('');
// Clean up any previous listener
if (mintQuoteUnsubscribeRef.current) {
mintQuoteUnsubscribeRef.current();
mintQuoteUnsubscribeRef.current = null;
}
// Listen for payment
const quoteId = quote.quote;
const invoiceAmount = amount;
const unsubscribe = coco.on('mint-quote:redeemed', (payload: unknown) => {
const redeemedQuote = payload as { quoteId?: string };
if (redeemedQuote.quoteId === quoteId) {
toast({
title: 'Payment received',
description: `Successfully received ${invoiceAmount} sats!`
});
setShowInvoiceModal(false);
setCurrentInvoice(null);
mintQuoteUnsubscribeRef.current = null;
unsubscribe();
}
});
mintQuoteUnsubscribeRef.current = unsubscribe;
} catch (err) {
toast({
variant: 'destructive',
title: 'Failed to create invoice',
description: err instanceof Error ? err.message : 'Unknown error'
});
}
}, [coco, quoteAmount, activeMintUrl, generateQRCode, toast]);
// Handle send (pay Lightning invoice)
const handleLightningSend = useCallback(async () => {
if (isProcessingPayment || !coco || !lightningInvoice || !activeMintUrl) return;
setIsProcessingPayment(true);
try {
const invoice = lightningInvoice.trim();
// Create and pay melt quote
const meltQuote = await coco.quotes.createMeltQuote(activeMintUrl, invoice);
await coco.quotes.payMeltQuote(activeMintUrl, meltQuote.quote);
toast({
title: 'Payment sent',
description: 'Lightning payment completed successfully'
});
setLightningInvoice('');
} catch (err) {
toast({
variant: 'destructive',
title: 'Payment failed',
description: err instanceof Error ? err.message : 'Unknown error'
});
} finally {
setIsProcessingPayment(false);
}
}, [coco, lightningInvoice, activeMintUrl, isProcessingPayment, toast]);
// Cleanup listener on unmount
useCallback(() => {
return () => {
if (mintQuoteUnsubscribeRef.current) {
mintQuoteUnsubscribeRef.current();
}
};
}, []);
return {
// Receive
quoteAmount,
setQuoteAmount,
showInvoiceModal,
setShowInvoiceModal,
currentInvoice,
setCurrentInvoice,
handleLightningReceive,
// Send
lightningInvoice,
setLightningInvoice,
isProcessingPayment,
handleLightningSend,
};
}