RelayClientTestPage.tsx
1 import React, { useEffect, useState } from "react"; 2 import { IRelayEndpoint, RelayClient } from "@massmarket/client"; 3 import type { WalletClient } from "viem"; 4 import type { Account } from "viem/accounts"; 5 6 interface RelayClientTestPageProps { 7 relayEndpoint: IRelayEndpoint; 8 walletClient: WalletClient; 9 keycard: Account; 10 shopId: bigint; 11 } 12 13 function relativeTime(diff: number) { 14 // Convert milliseconds to seconds 15 const seconds = Math.floor(diff / 1000); 16 17 if (seconds < 60) { 18 return `${seconds} second${seconds !== 1 ? "s" : ""} ago`; 19 } 20 21 const minutes = Math.floor(seconds / 60); 22 if (minutes < 60) { 23 return `${minutes} minute${minutes !== 1 ? "s" : ""} ago`; 24 } 25 26 const hours = Math.floor(minutes / 60); 27 if (hours < 24) { 28 return `${hours} hour${hours !== 1 ? "s" : ""} ago`; 29 } 30 31 const days = Math.floor(hours / 24); 32 return `${days} day${days !== 1 ? "s" : ""} ago`; 33 } 34 35 const RelayClientTestPage: React.FC<RelayClientTestPageProps> = ({ 36 relayEndpoint, 37 walletClient, 38 keycard, 39 shopId, 40 }) => { 41 const [connectionStatus, setConnectionStatus] = useState< 42 "idle" | "connecting" | "connected" | "failed" | "disconnected" 43 >("idle"); 44 const [lastPingReceived, setLastPingReceived] = useState<string>("Never"); 45 const [error, setError] = useState<string | null>(null); 46 const [client, setClient] = useState<RelayClient | null>(null); 47 48 useEffect(() => { 49 const newClient = new RelayClient({ 50 relayEndpoint, 51 walletClient, 52 keycard, 53 shopId, 54 }); 55 56 setClient(newClient); 57 setConnectionStatus("connecting"); 58 59 // Connect to relay 60 newClient.connect((error: Event) => { 61 setError((error as ErrorEvent).message); 62 setConnectionStatus("failed"); 63 }).then(() => { 64 setConnectionStatus("connected"); 65 }); 66 67 // Cleanup function to disconnect when component unmounts 68 return () => { 69 if (client) { 70 client.disconnect().catch((err) => { 71 console.error("Error disconnecting:", err); 72 }); 73 } 74 }; 75 }, [relayEndpoint, walletClient, keycard, shopId]); 76 77 const handleDisconnect = async () => { 78 if (client) { 79 try { 80 await client.disconnect(); 81 setConnectionStatus("disconnected"); 82 } catch (err: unknown) { 83 setError(err instanceof Error ? err.message : String(err)); 84 setConnectionStatus("failed"); 85 setLastPingReceived("Never"); 86 } 87 } 88 }; 89 90 useEffect(() => { 91 const timer = setInterval(() => { 92 if (client && client.stats.pingsReceived > 0) { 93 console.log({ stats: client.stats }); 94 const now = new Date(); 95 const diff = now.getTime() - client?.stats.lastPingReceived.getTime(); 96 setLastPingReceived(relativeTime(diff)); 97 } 98 }, 1000); 99 100 return () => { 101 clearInterval(timer); 102 }; 103 }, [client]); 104 105 return ( 106 <div data-testid="relay-client-tester"> 107 <div data-testid="connection-status">Status: {connectionStatus}</div> 108 {error && <div data-testid="connection-error">Error: {error}</div>} 109 {connectionStatus === "connected" && ( 110 <button 111 type="button" 112 data-testid="disconnect-button" 113 onClick={handleDisconnect} 114 > 115 Disconnect 116 </button> 117 )} 118 <div data-testid="stats-last-ping-received"> 119 Last ping received: {lastPingReceived} 120 </div> 121 </div> 122 ); 123 }; 124 125 export default RelayClientTestPage;