import React, { useState, useEffect } from 'react'; import { useAccount, useSignMessage, useEnsName } from 'wagmi'; import type { LightNode } from '@waku/interfaces'; import { useWaku } from '@waku/react'; import { createMessage, encoder, BlockPayload } from '@/lib/waku'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Loader2 } from "lucide-react"; import QRCode from '@/components/QRCode'; import { useWalletPrompt } from '@/hooks/useWalletPrompt'; import { v4 as uuidv4 } from 'uuid'; import { fromLightPush, Telemetry } from '@/lib/telemetry'; interface SignChainProps { block: BlockPayload; chainsData: BlockPayload[]; onSuccess: (newBlock: BlockPayload) => void; } const SignChain: React.FC = ({ block, chainsData, onSuccess }) => { const [isOpen, setIsOpen] = useState(false); const [isSigning, setIsSigning] = useState(false); const [error, setError] = useState(null); const [alreadySigned, setAlreadySigned] = useState(false); const [isWalletPrompt, setIsWalletPrompt] = useState(false); const { address } = useAccount(); const { data: ensName } = useEnsName({ address }); const { node } = useWaku(); const { ensureWalletConnected } = useWalletPrompt(); const checkSignatures = (blockToCheck: BlockPayload, visitedBlocks: Set): boolean => { if (visitedBlocks.has(blockToCheck.blockUUID)) return false; visitedBlocks.add(blockToCheck.blockUUID); // Check current block signatures if (blockToCheck.signatures?.some(sig => sig?.address?.toLowerCase() === address?.toLowerCase())) { return true; } // Check parent block const parentBlock = chainsData.find(b => b.blockUUID === blockToCheck.parentBlockUUID); if (parentBlock && checkSignatures(parentBlock, visitedBlocks)) { return true; } // Check immediate child blocks return chainsData .filter(b => b.parentBlockUUID === blockToCheck.blockUUID) .some(childBlock => checkSignatures(childBlock, visitedBlocks)); }; useEffect(() => { if (!address) return; try { const visitedBlocks = new Set(); setAlreadySigned(checkSignatures(block, visitedBlocks)); } catch (error) { console.error('Error in signature check:', error); setAlreadySigned(false); } }, [address, block, chainsData]); const { signMessage } = useSignMessage({ mutation: { onMutate() { setError(null); setIsSigning(true); }, async onSuccess(signature) { if (!address || !node) return; try { if (block.signatures.some(sig => sig.address.toLowerCase() === address.toLowerCase())) { setError('You have already signed this chain.'); return; } const timestamp = Date.now(); const newBlock: BlockPayload = { chainUUID: block.chainUUID, blockUUID: uuidv4(), title: block.title, description: block.description, signedMessage: signature, timestamp, signatures: [{ address, signature }], parentBlockUUID: block.blockUUID }; const wakuMessage = createMessage(newBlock); const result = await node.lightPush.send(encoder, wakuMessage); Telemetry.push(fromLightPush({ result, node, encoder, timestamp, bookId: block.chainUUID, wallet: address, })); const { failures, successes } = result; if (failures.length > 0 || successes.length === 0) { throw new Error('Failed to send message to Waku network'); } onSuccess(newBlock); setIsOpen(false); } catch (error) { console.error('Error creating new block:', error); setError('Failed to create new block. Please try again.'); } }, onError(error) { console.error('Error signing message:', error); setError('Error signing message. Please try again. If using a mobile wallet, please ensure your wallet app is open.'); }, onSettled() { setIsSigning(false); } } }); const handleSign = async () => { try { if (!address) { setIsWalletPrompt(true); const connected = await ensureWalletConnected(); if (!connected) { setError('Please ensure your wallet is connected and the app is open.'); return; } } if (alreadySigned) { setError('You have already signed this chain.'); return; } const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); if (isMobile && typeof window.ethereum === 'undefined') { setError('Please ensure your wallet app is installed and open before signing.'); window.location.href = 'metamask:///'; return; } const message = [ 'Sign Block:', `Chain UUID: ${block.chainUUID}`, `Block UUID: ${block.blockUUID}`, `Title: ${block.title}`, `Description: ${block.description}`, `Timestamp: ${new Date().getTime()}`, `Parent Block UUID: ${block.parentBlockUUID}`, `Signed by: ${ensName || address}` ].join('\n'); signMessage({ message }); } catch (error) { console.error('Error in sign flow:', error); setError('Failed to initiate signing. Please ensure your wallet app is open and try again.'); setIsSigning(false); } finally { setIsWalletPrompt(false); } }; const getButtonText = () => { if (isSigning) return 'Signing...'; if (isWalletPrompt) return 'Connecting...'; if (alreadySigned) return 'Already Signed'; if (!address) return 'Connect Wallet'; return 'Sign'; }; const showLoadingSpinner = isSigning || isWalletPrompt; return ( <> Sign Book {alreadySigned ? 'You have already signed this book.' : 'Review the block details and sign to add your signature to the book.'}

Block Details

{block.title}

{block.description}

{(error || isWalletPrompt) && (
{error &&

{error}

} {isWalletPrompt && (

Attempting to connect to your wallet...

If your wallet doesn't open automatically, please open it manually to approve the connection.

)}
)}
); }; export default SignChain;