Choose Action
{action === 'create' ? 'Create' : 'Join'} by...
Enter Details
Connected
Peer: {connectedPeer || 'Unknown'} • ID: {currentConnectionId}
No messages yet. Start chatting!
) : ( messages.map((msg, idx) => (import { useState, useEffect, useRef } from 'react'; import { Rondevu, RondevuClient } from '@xtr-dev/rondevu-client'; const rdv = new Rondevu({ baseUrl: 'https://rondevu.xtrdev.workers.dev', rtcConfig: { iceServers: [ { urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun1.l.google.com:19302' }, { urls: 'turn:relay1.expressturn.com:3480', username: 'ef13B1E5PH265HK1N2', credential: 'TTcTPEy3ndxsS0Gp' } ] } }); const client = new RondevuClient({ baseUrl: 'https://rondevu.xtrdev.workers.dev' }); function App() { // Step-based state const [step, setStep] = useState(1); // 1: action, 2: method, 3: details, 4: connected const [action, setAction] = useState(null); // 'create' or 'join' const [method, setMethod] = useState(null); // 'topic', 'peer-id', 'connection-id' // Connection state const [topic, setTopic] = useState(''); const [connectionId, setConnectionId] = useState(''); const [peerId, setPeerId] = useState(''); const [topics, setTopics] = useState([]); const [sessions, setSessions] = useState([]); const [connectionStatus, setConnectionStatus] = useState('disconnected'); const [connectedPeer, setConnectedPeer] = useState(null); const [currentConnectionId, setCurrentConnectionId] = useState(null); // Chat state const [messages, setMessages] = useState([]); const [messageInput, setMessageInput] = useState(''); const [logs, setLogs] = useState([]); const connectionRef = useRef(null); const dataChannelRef = useRef(null); useEffect(() => { log('Demo initialized', 'info'); loadTopics(); }, []); const log = (message, type = 'info') => { const timestamp = new Date().toLocaleTimeString(); setLogs(prev => [...prev, { message, type, timestamp }]); }; const loadTopics = async () => { try { const { topics } = await client.listTopics(); setTopics(topics); } catch (error) { log(`Error loading topics: ${error.message}`, 'error'); } }; const discoverPeers = async (topicName) => { try { const { sessions: foundSessions } = await client.listSessions(topicName); const otherSessions = foundSessions.filter(s => s.peerId !== rdv.peerId); setSessions(otherSessions); } catch (error) { log(`Error discovering peers: ${error.message}`, 'error'); } }; const setupConnection = (connection) => { connectionRef.current = connection; connection.on('connect', () => { log('✅ Connected!', 'success'); setConnectionStatus('connected'); setStep(4); const channel = connection.dataChannel('chat'); setupDataChannel(channel); }); connection.on('disconnect', () => { log('Disconnected', 'info'); reset(); }); connection.on('error', (error) => { log(`Error: ${error.message}`, 'error'); if (error.message.includes('timeout')) { reset(); } }); connection.on('datachannel', (channel) => { if (channel.label === 'chat') { setupDataChannel(channel); } }); }; const setupDataChannel = (channel) => { dataChannelRef.current = channel; channel.onmessage = (event) => { setMessages(prev => [...prev, { text: event.data, type: 'received', timestamp: new Date() }]); }; }; const handleConnect = async () => { try { setConnectionStatus('connecting'); log('Connecting...', 'info'); let connection; if (action === 'create') { if (method === 'connection-id') { const id = connectionId || `conn-${Date.now()}`; connection = await rdv.create(id, topic || 'default'); setCurrentConnectionId(id); log(`Created connection: ${id}`, 'success'); } else { const id = `conn-${Date.now()}`; connection = await rdv.create(id, topic); setCurrentConnectionId(id); log(`Created connection: ${id}`, 'success'); } } else { if (method === 'topic') { connection = await rdv.join(topic); setCurrentConnectionId(connection.id); } else if (method === 'peer-id') { connection = await rdv.join(topic, { filter: (s) => s.peerId === peerId }); setCurrentConnectionId(connection.id); } else if (method === 'connection-id') { connection = await rdv.connect(connectionId); setCurrentConnectionId(connectionId); } } setConnectedPeer(connection.remotePeerId || 'Waiting...'); setupConnection(connection); } catch (error) { log(`Error: ${error.message}`, 'error'); setConnectionStatus('disconnected'); } }; const sendMessage = () => { if (!messageInput || !dataChannelRef.current || dataChannelRef.current.readyState !== 'open') { return; } dataChannelRef.current.send(messageInput); setMessages(prev => [...prev, { text: messageInput, type: 'sent', timestamp: new Date() }]); setMessageInput(''); }; const reset = () => { if (connectionRef.current) { connectionRef.current.close(); } setStep(1); setAction(null); setMethod(null); setTopic(''); setConnectionId(''); setPeerId(''); setSessions([]); setConnectionStatus('disconnected'); setConnectedPeer(null); setCurrentConnectionId(null); setMessages([]); connectionRef.current = null; dataChannelRef.current = null; }; return (
Peer: {connectedPeer || 'Unknown'} • ID: {currentConnectionId}
No messages yet. Start chatting!
) : ( messages.map((msg, idx) => (