mirror of
https://github.com/xtr-dev/rondevu-demo.git
synced 2025-12-10 02:43:23 +00:00
Compare commits
75 Commits
v1.0.1
...
c511b15fbf
| Author | SHA1 | Date | |
|---|---|---|---|
| c511b15fbf | |||
| 71454e31d1 | |||
| b2a17ce42b | |||
| 3a42f74371 | |||
| 2cbd46b27a | |||
| b3dde85cd2 | |||
| 66dc35c1a7 | |||
| 74bf2757ff | |||
| 2550c1ac3f | |||
| 3d9a1c27bd | |||
| e26bdc308d | |||
| 6650f51038 | |||
| 0a975f4bcf | |||
| c889549362 | |||
| fb9830ea8c | |||
| 5714731d71 | |||
| 4ff5da0568 | |||
| d575022412 | |||
| 84ceae9a05 | |||
| c5f640bc62 | |||
| 9163e5166c | |||
| 7d3b19a2b0 | |||
| 7d19557966 | |||
| 70fd6bd16a | |||
| 6dece31f2d | |||
| b741e8f40c | |||
| 2c20af83c9 | |||
| 78c16c95f5 | |||
| 953f62ce81 | |||
| c46bfb40a9 | |||
| 50eeec5164 | |||
| 217b84701f | |||
| 273b6349c6 | |||
| 37c84b7553 | |||
| 2d1b2e8ff4 | |||
| c849f6a109 | |||
| 0348ef5d8e | |||
| 1ac1121793 | |||
| caedff590f | |||
| d922329437 | |||
| 4c58dee371 | |||
| e4868c085b | |||
| 9a424b6015 | |||
| 79030adb09 | |||
| 1f1502940b | |||
| 348a732178 | |||
| 9e761546e7 | |||
| f9fb74de53 | |||
| e5e28c8264 | |||
| 4021a02f6d | |||
| 73f04bc078 | |||
| 257d8f264a | |||
| 678f692b64 | |||
| 83df6aeee3 | |||
| c3045778eb | |||
| dc856b7abf | |||
| ed8709c6f6 | |||
| b27ab02552 | |||
| b321a01d5e | |||
| 2dc4c711e3 | |||
| b8637ed8ad | |||
| 94b2849971 | |||
| 65f4aaffe0 | |||
| e1c8c25ea8 | |||
| 600d6308b9 | |||
| adc363fed0 | |||
| d677f36eeb | |||
| eaf474a984 | |||
| 1efe7346f4 | |||
| 1bbce9295d | |||
| 6538b5a18f | |||
| ee440b083d | |||
| 60f1068bd1 | |||
| 55a3d0ba51 | |||
| 2b574526d1 |
91
CLAUDE.md
Normal file
91
CLAUDE.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Rondevu Demo Development Guidelines
|
||||
|
||||
## WebRTC Configuration
|
||||
|
||||
### TURN Server Setup
|
||||
|
||||
When configuring TURN servers:
|
||||
|
||||
- ✅ **DO** use TURNS (secure) on port 5349 when available: `turns:server.com:5349`
|
||||
- ✅ **DO** include TURN fallback on port 3478: `turn:server.com:3478`
|
||||
- ✅ **DO** include the port number in TURN URLs (even if default)
|
||||
- ✅ **DO** test TURN connectivity before deploying: `turnutils_uclient -u user -w pass server.com 3478 -y`
|
||||
- ✅ **DO** provide both TCP and UDP transports for maximum compatibility
|
||||
- ❌ **DON'T** omit the port number
|
||||
- ❌ **DON'T** assume TURN works without testing
|
||||
|
||||
**Current Configuration:**
|
||||
```javascript
|
||||
const RTC_CONFIG = {
|
||||
iceServers: [
|
||||
{ urls: ["stun:stun.share.fish:3478"] },
|
||||
{
|
||||
urls: [
|
||||
// TURNS (secure) - TLS/DTLS on port 5349 (preferred)
|
||||
"turns:turn.share.fish:5349?transport=tcp",
|
||||
"turns:turn.share.fish:5349?transport=udp",
|
||||
// TURN (fallback) - plain on port 3478
|
||||
"turn:turn.share.fish:3478?transport=tcp",
|
||||
"turn:turn.share.fish:3478?transport=udp",
|
||||
],
|
||||
username: "webrtcuser",
|
||||
credential: "supersecretpassword"
|
||||
}
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
WebRTC will try TURNS (secure) endpoints first, falling back to plain TURN if needed.
|
||||
|
||||
### ICE Configuration
|
||||
|
||||
**Force Relay Mode for Testing:**
|
||||
```javascript
|
||||
const RTC_CONFIG = {
|
||||
iceServers: [...],
|
||||
iceTransportPolicy: 'relay' // Forces TURN relay, bypasses NAT issues
|
||||
};
|
||||
```
|
||||
|
||||
Use `iceTransportPolicy: 'relay'` to:
|
||||
- Test if TURN server is working correctly
|
||||
- Bypass NAT hairpinning issues (when both peers are on same network)
|
||||
- Ensure maximum compatibility
|
||||
|
||||
**Remove or comment out** `iceTransportPolicy: 'relay'` for production to allow direct connections when possible.
|
||||
|
||||
## Debugging
|
||||
|
||||
### Enable Detailed ICE Logging
|
||||
|
||||
The demo includes detailed ICE candidate logging. Check browser console for:
|
||||
- 🧊 ICE candidate gathering
|
||||
- 🧊 ICE connection state changes
|
||||
- 📤 Candidates sent to server
|
||||
- 📥 Candidates received from server
|
||||
- ✅ Successful candidate pairs
|
||||
- ❌ Failed candidate pairs
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Connection stuck in "connecting":**
|
||||
- Enable relay-only mode to test TURN
|
||||
- Check if both peers are behind same NAT (hairpinning issue)
|
||||
- Verify TURN credentials are correct
|
||||
|
||||
2. **No candidates gathered:**
|
||||
- Check STUN/TURN server URLs
|
||||
- Verify firewall isn't blocking UDP ports
|
||||
- Check TURN server is running
|
||||
|
||||
3. **Candidates gathered but connection fails:**
|
||||
- Check if TURN relay is actually working (use `turnutils_uclient`)
|
||||
- Verify server is filtering candidates by role correctly
|
||||
- Enable detailed logging to see which candidate pairs are failing
|
||||
|
||||
## UI Guidelines
|
||||
|
||||
- Show clear connection status (waiting, connecting, connected, failed)
|
||||
- Display peer role (offerer vs answerer) for debugging
|
||||
- Provide visual feedback for all user actions
|
||||
- Use toast notifications for errors and success messages
|
||||
332
README.md
332
README.md
@@ -1,51 +1,54 @@
|
||||
# Rondevu
|
||||
# Rondevu Demo
|
||||
|
||||
🎯 **Simple WebRTC peer signaling and discovery**
|
||||
🎯 **Interactive WebRTC peer discovery and connection demo**
|
||||
|
||||
Meet peers by topic, by peer ID, or by connection ID.
|
||||
Experience topic-based peer discovery and WebRTC connections using the Rondevu signaling platform.
|
||||
|
||||
**Related repositories:**
|
||||
- [rondevu-server](https://github.com/xtr-dev/rondevu-server) - HTTP signaling server
|
||||
- [rondevu-client](https://github.com/xtr-dev/rondevu-client) - TypeScript client library
|
||||
- [@xtr-dev/rondevu-client](https://github.com/xtr-dev/rondevu-client) - TypeScript client library ([npm](https://www.npmjs.com/package/@xtr-dev/rondevu-client))
|
||||
- [@xtr-dev/rondevu-server](https://github.com/xtr-dev/rondevu-server) - HTTP signaling server ([npm](https://www.npmjs.com/package/@xtr-dev/rondevu-server), [live](https://api.ronde.vu))
|
||||
- [@xtr-dev/rondevu-demo](https://github.com/xtr-dev/rondevu-demo) - Interactive demo ([live](https://ronde.vu))
|
||||
|
||||
---
|
||||
|
||||
## Rondevu Demo
|
||||
## Overview
|
||||
|
||||
**Interactive demo showcasing three ways to connect WebRTC peers.**
|
||||
This demo showcases the complete Rondevu workflow:
|
||||
|
||||
Experience how easy WebRTC peer discovery can be with Rondevu's three connection methods:
|
||||
1. **Register** - Get peer credentials (automatically saved)
|
||||
2. **Create Offers** - Advertise your WebRTC connection on topics
|
||||
3. **Discover Peers** - Find other peers by topic
|
||||
4. **Connect** - Establish direct P2P WebRTC connections via `RondevuPeer`
|
||||
5. **Chat** - Send messages over WebRTC data channels
|
||||
|
||||
🎯 **Connect by Topic** - Auto-discover and join any available peer
|
||||
👤 **Connect by Peer ID** - Filter and connect to specific peers
|
||||
🔗 **Connect by Connection ID** - Share a code and connect directly
|
||||
### Key Features
|
||||
|
||||
### Features
|
||||
- **Topic-Based Discovery** - Find peers by shared topics (like torrent infohashes)
|
||||
- **Real P2P Connections** - Actual WebRTC data channels (not simulated)
|
||||
- **State-Based Peer Management** - Uses `RondevuPeer` with clean state machine (idle → creating-offer → waiting-for-answer → exchanging-ice → connected)
|
||||
- **Trickle ICE** - Fast connection establishment by sending ICE candidates as they're discovered
|
||||
- **Persistent Credentials** - Saves authentication to localStorage
|
||||
- **Topics Browser** - Browse all active topics and peer counts
|
||||
- **Multiple Connections** - Support multiple simultaneous peer connections
|
||||
- **Real-time Chat** - Direct peer-to-peer messaging
|
||||
|
||||
- **Three Connection Methods** - Experience topic discovery, peer filtering, and direct connection
|
||||
- **Real WebRTC** - Actual P2P connections using RTCPeerConnection (not simulated!)
|
||||
- **P2P Data Channel** - Direct peer-to-peer chat without server relay
|
||||
- **Peer Discovery** - Browse topics and discover available peers
|
||||
- **Real-time Chat** - Send and receive messages over WebRTC data channel
|
||||
- **Activity Log** - Monitor all API and WebRTC events
|
||||
## Quick Start
|
||||
|
||||
### Quick Start
|
||||
|
||||
#### Installation
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
#### Development
|
||||
### Development
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
This will start the Vite dev server at `http://localhost:5173`
|
||||
This starts the Vite dev server at `http://localhost:5173`
|
||||
|
||||
#### Build for Production
|
||||
### Build for Production
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@@ -53,150 +56,190 @@ npm run build
|
||||
|
||||
The built files will be in the `dist/` directory.
|
||||
|
||||
#### Preview Production Build
|
||||
### Preview Production Build
|
||||
|
||||
```bash
|
||||
npm run preview
|
||||
```
|
||||
|
||||
### Three Ways to Connect
|
||||
## How to Use
|
||||
|
||||
This demo demonstrates all three Rondevu connection methods:
|
||||
### Step 1: Register (One-time)
|
||||
|
||||
#### 1️⃣ Join Topic (Auto-Discovery)
|
||||
The demo automatically registers you when you first visit. Your credentials are saved in localStorage for future visits.
|
||||
|
||||
**Easiest method** - Just enter a topic and auto-connect to first available peer:
|
||||
### Step 2: Create an Offer
|
||||
|
||||
1. Enter a topic name in the "Join Topic" section (e.g., "demo-room")
|
||||
2. Click "Join Topic"
|
||||
3. Rondevu finds the first available peer and connects automatically
|
||||
4. Start chatting!
|
||||
1. Go to the "Create Offer" tab
|
||||
2. Enter one or more topics (comma-separated), e.g., `demo-room, testing`
|
||||
3. Click "Create Offer"
|
||||
4. Your offer is now advertised on those topics
|
||||
|
||||
**Best for:** Quick matching, joining any available game/chat
|
||||
**Share the topic name with peers you want to connect with!**
|
||||
|
||||
---
|
||||
### Step 3: Discover and Connect (Other Peer)
|
||||
|
||||
#### 2️⃣ Discover Peers (Filter by Peer ID)
|
||||
1. Go to the "Discover Offers" tab
|
||||
2. Enter the same topic (e.g., `demo-room`)
|
||||
3. Click "Discover Offers"
|
||||
4. See available peers and their offers
|
||||
5. Click "Answer Offer" to connect
|
||||
|
||||
**Connect to specific peers** - Browse and select which peer to connect to:
|
||||
### Step 4: Chat
|
||||
|
||||
1. Enter a topic name (e.g., "demo-room")
|
||||
2. Click "Discover in [topic]" to list all available peers
|
||||
3. See each peer's ID in the list
|
||||
4. Click "Connect" on the specific peer you want to talk to
|
||||
5. Start chatting!
|
||||
1. Once connected, go to the "Chat" tab
|
||||
2. Select a connection from the dropdown
|
||||
3. Type messages and hit Enter or click Send
|
||||
4. Messages are sent **directly peer-to-peer** via WebRTC
|
||||
|
||||
**Best for:** Connecting to friends, teammates, or specific users
|
||||
### Browse Topics
|
||||
|
||||
---
|
||||
Click the "Topics" tab to:
|
||||
- See all active topics
|
||||
- View peer counts for each topic
|
||||
- Quick-discover by clicking a topic
|
||||
|
||||
#### 3️⃣ Create/Connect by ID (Direct Connection)
|
||||
|
||||
**Share a connection code** - Like sharing a meeting link:
|
||||
|
||||
**To create:**
|
||||
1. Enter a topic name (e.g., "meetings")
|
||||
2. Enter a custom Connection ID (e.g., "my-meeting-123") or leave blank for auto-generation
|
||||
3. Click "Create Connection"
|
||||
4. **Share the Connection ID** with the person you want to connect with
|
||||
|
||||
**To join:**
|
||||
1. Get the Connection ID from your friend (e.g., "my-meeting-123")
|
||||
2. Enter it in the "Connect by ID" section
|
||||
3. Click "Connect to ID"
|
||||
4. Start chatting!
|
||||
|
||||
**Best for:** Meeting rooms, QR code connections, invitation-based sessions
|
||||
|
||||
#### Testing Locally
|
||||
## Testing Locally
|
||||
|
||||
The easiest way to test:
|
||||
1. Open the demo in **two different browser windows** (or tabs)
|
||||
2. In window 1: Create an offer with topic "test-room"
|
||||
3. In window 2: Discover peers in "test-room" and click Connect
|
||||
4. Watch the connection establish and start chatting!
|
||||
|
||||
#### Browse Topics
|
||||
1. Open the demo in **two browser windows** (or tabs)
|
||||
2. **Window 1**: Create an offer with topic `test-room`
|
||||
3. **Window 2**: Discover offers in `test-room` and answer
|
||||
4. Switch to Chat tab in both windows
|
||||
5. Start chatting peer-to-peer!
|
||||
|
||||
- Click "Refresh Topics" to see all active topics
|
||||
- Click on any topic to auto-fill the discovery form
|
||||
## Technical Implementation
|
||||
|
||||
### Server Configuration
|
||||
### RondevuPeer State Machine
|
||||
|
||||
This demo connects to: `https://rondevu.xtrdev.workers.dev`
|
||||
|
||||
To use a different server, modify the `baseUrl` in `src/main.js`:
|
||||
This demo uses the `RondevuPeer` class which implements a clean state-based connection lifecycle:
|
||||
|
||||
```javascript
|
||||
const client = new RondevuClient({
|
||||
baseUrl: 'https://your-server.com'
|
||||
import { Rondevu } from '@xtr-dev/rondevu-client';
|
||||
|
||||
// Create peer
|
||||
const peer = client.createPeer();
|
||||
|
||||
// Set up event listeners
|
||||
peer.on('state', (state) => {
|
||||
console.log('Peer state:', state);
|
||||
// Offerer: idle → creating-offer → waiting-for-answer → exchanging-ice → connected
|
||||
// Answerer: idle → answering → exchanging-ice → connected
|
||||
});
|
||||
|
||||
peer.on('connected', () => {
|
||||
console.log('✅ P2P connection established!');
|
||||
});
|
||||
|
||||
peer.on('datachannel', (channel) => {
|
||||
channel.addEventListener('message', (event) => {
|
||||
console.log('📥 Message:', event.data);
|
||||
});
|
||||
|
||||
channel.addEventListener('open', () => {
|
||||
// Channel is ready, can send messages
|
||||
channel.send('Hello!');
|
||||
});
|
||||
});
|
||||
|
||||
peer.on('failed', (error) => {
|
||||
console.error('❌ Connection failed:', error);
|
||||
});
|
||||
|
||||
// Create offer (offerer)
|
||||
await peer.createOffer({
|
||||
topics: ['demo-room'],
|
||||
ttl: 300000
|
||||
});
|
||||
|
||||
// Or answer an offer (answerer)
|
||||
await peer.answer(offerId, offerSdp, {
|
||||
topics: ['demo-room']
|
||||
});
|
||||
```
|
||||
|
||||
### Technologies
|
||||
### Connection States
|
||||
|
||||
- **Vite** - Fast development and build tool
|
||||
- **@xtr-dev/rondevu-client** - TypeScript client for Rondevu API
|
||||
- **Vanilla JavaScript** - No framework dependencies
|
||||
**Offerer Flow:**
|
||||
1. **idle** - Initial state
|
||||
2. **creating-offer** - Creating WebRTC offer and sending to server
|
||||
3. **waiting-for-answer** - Polling for answer from peer (every 2 seconds)
|
||||
4. **exchanging-ice** - Exchanging ICE candidates (polling every 1 second)
|
||||
5. **connected** - Successfully connected!
|
||||
6. **failed/closed** - Connection failed or was closed
|
||||
|
||||
### API Examples
|
||||
**Answerer Flow:**
|
||||
1. **idle** - Initial state
|
||||
2. **answering** - Creating WebRTC answer and sending to server
|
||||
3. **exchanging-ice** - Exchanging ICE candidates (polling every 1 second)
|
||||
4. **connected** - Successfully connected!
|
||||
5. **failed/closed** - Connection failed or was closed
|
||||
|
||||
The demo showcases all major Rondevu API endpoints:
|
||||
### What Happens Under the Hood
|
||||
|
||||
- `GET /` - List all topics
|
||||
- `GET /:topic/sessions` - Discover peers in a topic
|
||||
- `POST /:topic/offer` - Create a new offer
|
||||
- `POST /answer` - Send answer to a peer
|
||||
- `POST /poll` - Poll for peer data
|
||||
- `GET /health` - Check server health
|
||||
1. **Offerer** calls `peer.createOffer()`:
|
||||
- State → `creating-offer`
|
||||
- Creates RTCPeerConnection and data channel
|
||||
- Generates SDP offer
|
||||
- Sets up ICE candidate handler (before gathering starts)
|
||||
- Sets local description → ICE gathering begins
|
||||
- Posts offer to Rondevu server
|
||||
- State → `waiting-for-answer`
|
||||
- Polls for answers every 2 seconds
|
||||
- When answer received → State → `exchanging-ice`
|
||||
|
||||
### WebRTC Implementation Details
|
||||
2. **Answerer** calls `peer.answer()`:
|
||||
- State → `answering`
|
||||
- Creates RTCPeerConnection
|
||||
- Sets remote description (offer SDP)
|
||||
- Generates SDP answer
|
||||
- Sends answer to server (registers as answerer)
|
||||
- Sets up ICE candidate handler (before gathering starts)
|
||||
- Sets local description → ICE gathering begins
|
||||
- State → `exchanging-ice`
|
||||
|
||||
This demo implements a **complete WebRTC peer-to-peer connection** with:
|
||||
3. **ICE Exchange** (Trickle ICE):
|
||||
- Both peers generate ICE candidates as they're discovered
|
||||
- Candidates are automatically sent to server immediately
|
||||
- Peers poll and receive remote candidates (every 1 second)
|
||||
- ICE establishes the direct P2P path
|
||||
- State → `connected`
|
||||
|
||||
#### Connection Flow
|
||||
4. **Connection Established**:
|
||||
- Data channel opens
|
||||
- Chat messages flow directly between peers
|
||||
- No server relay (true P2P!)
|
||||
|
||||
1. **Offerer** creates an `RTCPeerConnection` and generates an SDP offer
|
||||
2. Offer is sent to the Rondevu signaling server via `POST /:topic/offer`
|
||||
3. **Answerer** discovers the offer via `GET /:topic/sessions`
|
||||
4. Answerer creates an `RTCPeerConnection`, sets the remote offer, and generates an SDP answer
|
||||
5. Answer is sent via `POST /answer`
|
||||
6. Both peers generate ICE candidates and send them via `POST /answer` with `candidate` field
|
||||
7. Both peers poll via `POST /poll` to receive remote ICE candidates
|
||||
8. Once candidates are exchanged, the **direct P2P connection** is established
|
||||
9. Data channel opens and chat messages flow **directly between peers**
|
||||
### Key Features of Implementation
|
||||
|
||||
#### Key Features
|
||||
- **Trickle ICE**: Candidates sent immediately as discovered (no waiting)
|
||||
- **Proper Authorization**: Answer sent to server before ICE gathering to authorize candidate posting
|
||||
- **Event Cleanup**: All event listeners properly removed with `removeEventListener`
|
||||
- **State Management**: Clean state machine with well-defined transitions
|
||||
- **Error Handling**: Graceful failure states with error events
|
||||
|
||||
- **Real RTCPeerConnection** - Not simulated, actual WebRTC
|
||||
- **STUN servers** - Google's public STUN servers for NAT traversal
|
||||
- **Data Channel** - Named "chat" channel for text messaging
|
||||
- **ICE Trickle** - Candidates are sent as they're generated
|
||||
- **Automatic Polling** - Polls every 1 second for remote data
|
||||
- **Connection States** - Visual indicators for connecting/connected/failed states
|
||||
- **Graceful Cleanup** - Properly closes connections and stops polling
|
||||
### Architecture
|
||||
|
||||
#### Technologies
|
||||
- **Frontend**: React + Vite
|
||||
- **Signaling**: Rondevu server (Cloudflare Workers + D1)
|
||||
- **Client**: @xtr-dev/rondevu-client (TypeScript library)
|
||||
- **WebRTC**: RTCPeerConnection with STUN/TURN servers
|
||||
- **Connection Management**: RondevuPeer class with state machine
|
||||
|
||||
- **RTCPeerConnection API** - Core WebRTC connection
|
||||
- **RTCDataChannel API** - Unreliable but fast text messaging
|
||||
- **Rondevu Signaling** - SDP and ICE candidate exchange
|
||||
- **STUN Protocol** - NAT traversal (stun.l.google.com)
|
||||
## Server Configuration
|
||||
|
||||
### Development Notes
|
||||
This demo connects to: `https://api.ronde.vu`
|
||||
|
||||
- Peer IDs are auto-generated on page load
|
||||
- WebRTC connections use **real** RTCPeerConnection (not simulated!)
|
||||
- Sessions expire after the server's configured timeout (5 minutes default)
|
||||
- The demo is completely client-side (no backend required)
|
||||
- Messages are sent P2P - the server only facilitates discovery
|
||||
- Works across different browsers and networks (with STUN support)
|
||||
To use a different server, modify `API_URL` in `src/App.jsx`:
|
||||
|
||||
### Deployment
|
||||
```javascript
|
||||
const API_URL = 'https://your-server.com';
|
||||
```
|
||||
|
||||
#### Deploy to Cloudflare Pages
|
||||
## Deployment
|
||||
|
||||
The demo can be easily deployed to Cloudflare Pages (free tier):
|
||||
### Deploy to Cloudflare Pages
|
||||
|
||||
**Quick Deploy via Wrangler:**
|
||||
|
||||
@@ -213,7 +256,46 @@ npx wrangler pages deploy dist --project-name=rondevu-demo
|
||||
4. Set output directory: `dist`
|
||||
5. Deploy automatically on every push!
|
||||
|
||||
### License
|
||||
## Development Notes
|
||||
|
||||
- Credentials are stored in localStorage and persist across sessions
|
||||
- Offers expire after 5 minutes by default
|
||||
- The peer automatically polls for answers and ICE candidates
|
||||
- Multiple simultaneous connections are supported
|
||||
- WebRTC uses Google's public STUN servers + custom TURN server for NAT traversal
|
||||
- Data channel messages are unreliable but fast (perfect for chat)
|
||||
- Connection cleanup is automatic when peers disconnect
|
||||
|
||||
## Connection Timeouts
|
||||
|
||||
The demo uses these default timeouts:
|
||||
|
||||
- **ICE Gathering**: 10 seconds (not used with trickle ICE)
|
||||
- **Waiting for Answer**: 30 seconds
|
||||
- **Creating Answer**: 10 seconds
|
||||
- **ICE Connection**: 30 seconds
|
||||
|
||||
These can be customized in the `PeerOptions`:
|
||||
|
||||
```javascript
|
||||
await peer.createOffer({
|
||||
topics: ['my-topic'],
|
||||
timeouts: {
|
||||
waitingForAnswer: 60000, // 1 minute
|
||||
iceConnection: 45000 // 45 seconds
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Technologies
|
||||
|
||||
- **React** - UI framework
|
||||
- **Vite** - Build tool and dev server
|
||||
- **@xtr-dev/rondevu-client** - Rondevu client library with `RondevuPeer`
|
||||
- **RTCPeerConnection** - WebRTC connections
|
||||
- **RTCDataChannel** - P2P messaging
|
||||
- **QRCode** - QR code generation for easy topic sharing
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
|
||||
298
package-lock.json
generated
298
package-lock.json
generated
@@ -1,17 +1,19 @@
|
||||
{
|
||||
"name": "rondevu-demo",
|
||||
"version": "1.0.1",
|
||||
"version": "2.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "rondevu-demo",
|
||||
"version": "1.0.1",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@xtr-dev/rondevu-client": "^0.9.2",
|
||||
"@zxing/library": "^0.21.3",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hot-toast": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.0",
|
||||
@@ -20,15 +22,6 @@
|
||||
"vite": "^5.4.11"
|
||||
}
|
||||
},
|
||||
"../client": {
|
||||
"name": "@xtr-dev/rondevu-client",
|
||||
"version": "0.0.4",
|
||||
"extraneous": true,
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
||||
@@ -752,6 +745,15 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/ed25519": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-3.0.0.tgz",
|
||||
"integrity": "sha512-QyteqMNm0GLqfa5SoYbSC3+Pvykwpn95Zgth4MFVSMKBB75ELl9tX1LAVsN4c3HXOrakHsF2gL4zWDAYCcsnzg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/pluginutils": {
|
||||
"version": "1.0.0-beta.27",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
|
||||
@@ -760,9 +762,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz",
|
||||
"integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
|
||||
"integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -774,9 +776,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz",
|
||||
"integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
|
||||
"integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -788,9 +790,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz",
|
||||
"integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
|
||||
"integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -802,9 +804,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz",
|
||||
"integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
|
||||
"integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -816,9 +818,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz",
|
||||
"integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
|
||||
"integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -830,9 +832,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz",
|
||||
"integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
|
||||
"integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -844,9 +846,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz",
|
||||
"integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
|
||||
"integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -858,9 +860,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz",
|
||||
"integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
|
||||
"integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -872,9 +874,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz",
|
||||
"integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
|
||||
"integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -886,9 +888,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz",
|
||||
"integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
|
||||
"integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -900,9 +902,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz",
|
||||
"integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
|
||||
"integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -914,9 +916,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz",
|
||||
"integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
|
||||
"integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -928,9 +930,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz",
|
||||
"integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
|
||||
"integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -942,9 +944,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz",
|
||||
"integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
|
||||
"integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -956,9 +958,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz",
|
||||
"integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
|
||||
"integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -970,9 +972,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz",
|
||||
"integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
|
||||
"integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -984,9 +986,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz",
|
||||
"integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz",
|
||||
"integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -998,9 +1000,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz",
|
||||
"integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
|
||||
"integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1012,9 +1014,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz",
|
||||
"integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
|
||||
"integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1026,9 +1028,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz",
|
||||
"integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
|
||||
"integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -1040,9 +1042,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz",
|
||||
"integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
|
||||
"integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1054,9 +1056,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz",
|
||||
"integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
|
||||
"integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1127,14 +1129,14 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.3.26",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.26.tgz",
|
||||
"integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==",
|
||||
"version": "18.3.27",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
|
||||
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-dom": {
|
||||
@@ -1168,6 +1170,15 @@
|
||||
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@xtr-dev/rondevu-client": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@xtr-dev/rondevu-client/-/rondevu-client-0.9.2.tgz",
|
||||
"integrity": "sha512-DVow5AOPU40dqQtlfQK7J2GNX8dz2/4UzltMqublaPZubbkRYgocvp0b76NQu5F6v150IstMV2N49uxAYqogVw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/ed25519": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@zxing/library": {
|
||||
"version": "0.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.21.3.tgz",
|
||||
@@ -1215,9 +1226,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.8.23",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.23.tgz",
|
||||
"integrity": "sha512-616V5YX4bepJFzNyOfce5Fa8fDJMfoxzOIzDCZwaGL8MKVpFrXqfNUoIpRn9YMI5pXf/VKgzjB4htFMsFKKdiQ==",
|
||||
"version": "2.9.3",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.3.tgz",
|
||||
"integrity": "sha512-8QdH6czo+G7uBsNo0GiUfouPN1lRzKdJTGnKXwe12gkFbnnOUaUKGN55dMkfy+mnxmvjwl9zcI4VncczcVXDhA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
@@ -1225,9 +1236,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.27.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz",
|
||||
"integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
|
||||
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -1245,11 +1256,11 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.19",
|
||||
"caniuse-lite": "^1.0.30001751",
|
||||
"electron-to-chromium": "^1.5.238",
|
||||
"node-releases": "^2.0.26",
|
||||
"update-browserslist-db": "^1.1.4"
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
"electron-to-chromium": "^1.5.263",
|
||||
"node-releases": "^2.0.27",
|
||||
"update-browserslist-db": "^1.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
@@ -1268,9 +1279,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001753",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001753.tgz",
|
||||
"integrity": "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==",
|
||||
"version": "1.0.30001759",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001759.tgz",
|
||||
"integrity": "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -1325,10 +1336,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"dev": true,
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
@@ -1365,9 +1375,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.244",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.244.tgz",
|
||||
"integrity": "sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==",
|
||||
"version": "1.5.266",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.266.tgz",
|
||||
"integrity": "sha512-kgWEglXvkEfMH7rxP5OSZZwnaDWT7J9EoZCujhnpLbfi0bbNtRkgdX2E3gt0Uer11c61qCYktB3hwkAS325sJg==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -1473,6 +1483,15 @@
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/goober": {
|
||||
"version": "2.1.18",
|
||||
"resolved": "https://registry.npmjs.org/goober/-/goober-2.1.18.tgz",
|
||||
"integrity": "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"csstype": "^3.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
@@ -1713,6 +1732,23 @@
|
||||
"react": "^18.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-hot-toast": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz",
|
||||
"integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"csstype": "^3.1.3",
|
||||
"goober": "^2.1.16"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16",
|
||||
"react-dom": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/react-refresh": {
|
||||
"version": "0.17.0",
|
||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
|
||||
@@ -1739,9 +1775,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.52.5",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz",
|
||||
"integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==",
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
|
||||
"integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1755,28 +1791,28 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.52.5",
|
||||
"@rollup/rollup-android-arm64": "4.52.5",
|
||||
"@rollup/rollup-darwin-arm64": "4.52.5",
|
||||
"@rollup/rollup-darwin-x64": "4.52.5",
|
||||
"@rollup/rollup-freebsd-arm64": "4.52.5",
|
||||
"@rollup/rollup-freebsd-x64": "4.52.5",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.52.5",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.52.5",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.52.5",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.52.5",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.52.5",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.52.5",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.52.5",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.52.5",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.52.5",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.52.5",
|
||||
"@rollup/rollup-linux-x64-musl": "4.52.5",
|
||||
"@rollup/rollup-openharmony-arm64": "4.52.5",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.52.5",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.52.5",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.52.5",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.52.5",
|
||||
"@rollup/rollup-android-arm-eabi": "4.53.3",
|
||||
"@rollup/rollup-android-arm64": "4.53.3",
|
||||
"@rollup/rollup-darwin-arm64": "4.53.3",
|
||||
"@rollup/rollup-darwin-x64": "4.53.3",
|
||||
"@rollup/rollup-freebsd-arm64": "4.53.3",
|
||||
"@rollup/rollup-freebsd-x64": "4.53.3",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.53.3",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.53.3",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.53.3",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.53.3",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.53.3",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.53.3",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.53.3",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.53.3",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.53.3",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.53.3",
|
||||
"@rollup/rollup-linux-x64-musl": "4.53.3",
|
||||
"@rollup/rollup-openharmony-arm64": "4.53.3",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.53.3",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.53.3",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.53.3",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.53.3",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@@ -1851,9 +1887,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
|
||||
"integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
|
||||
"integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "rondevu-demo",
|
||||
"version": "1.0.1",
|
||||
"description": "Demo application for Rondevu peer signaling and discovery",
|
||||
"version": "2.0.0",
|
||||
"description": "Demo application for Rondevu DNS-like WebRTC with username claiming and service discovery",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -10,10 +10,12 @@
|
||||
"deploy": "npm run build && npx wrangler pages deploy dist --project-name=rondevu-demo"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xtr-dev/rondevu-client": "^0.9.2",
|
||||
"@zxing/library": "^0.21.3",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hot-toast": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.0",
|
||||
|
||||
1834
src/App.jsx
1834
src/App.jsx
File diff suppressed because it is too large
Load Diff
@@ -2,95 +2,31 @@ import QRCodeDisplay from './QRCodeDisplay';
|
||||
|
||||
function ConnectionForm({
|
||||
action,
|
||||
method,
|
||||
topic,
|
||||
setTopic,
|
||||
connectionId,
|
||||
setConnectionId,
|
||||
peerId,
|
||||
setPeerId,
|
||||
topics,
|
||||
sessions,
|
||||
connectionStatus,
|
||||
qrCodeUrl,
|
||||
currentConnectionId,
|
||||
onConnect,
|
||||
onBack,
|
||||
onTopicSelect,
|
||||
onDiscoverPeers
|
||||
onBack
|
||||
}) {
|
||||
return (
|
||||
<div className="step-container">
|
||||
<h2>Enter Details</h2>
|
||||
<h2>{action === 'create' ? 'Create Connection' : 'Join Connection'}</h2>
|
||||
<div className="form-container">
|
||||
{(method === 'topic' || method === 'peer-id' || (method === 'connection-id' && action === 'create')) && (
|
||||
<div className="form-group">
|
||||
<label>Topic</label>
|
||||
<input
|
||||
type="text"
|
||||
value={topic}
|
||||
onChange={(e) => setTopic(e.target.value)}
|
||||
placeholder="e.g., game-room"
|
||||
autoFocus
|
||||
/>
|
||||
{topics.length > 0 && (
|
||||
<div className="topic-list">
|
||||
{topics.map((t) => (
|
||||
<button
|
||||
key={t.topic}
|
||||
className="topic-item"
|
||||
onClick={() => {
|
||||
onTopicSelect(t.topic);
|
||||
if (method === 'peer-id') {
|
||||
onDiscoverPeers(t.topic);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t.topic} <span className="peer-count">({t.count})</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{method === 'peer-id' && (
|
||||
<div className="form-group">
|
||||
<label>Peer ID</label>
|
||||
<input
|
||||
type="text"
|
||||
value={peerId}
|
||||
onChange={(e) => setPeerId(e.target.value)}
|
||||
placeholder="e.g., player-123"
|
||||
/>
|
||||
{sessions.length > 0 && (
|
||||
<div className="topic-list">
|
||||
{sessions.map((s) => (
|
||||
<button
|
||||
key={s.code}
|
||||
className="topic-item"
|
||||
onClick={() => setPeerId(s.peerId)}
|
||||
>
|
||||
{s.peerId}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{method === 'connection-id' && (
|
||||
<div className="form-group">
|
||||
<label>Connection ID {action === 'create' && '(optional)'}</label>
|
||||
<input
|
||||
type="text"
|
||||
value={connectionId}
|
||||
onChange={(e) => setConnectionId(e.target.value)}
|
||||
placeholder={action === 'create' ? 'Auto-generated if empty' : 'e.g., meeting-123'}
|
||||
autoFocus={action === 'join'}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="form-group">
|
||||
<label>Connection ID {action === 'create' && '(optional)'}</label>
|
||||
<input
|
||||
type="text"
|
||||
value={connectionId}
|
||||
onChange={(e) => setConnectionId(e.target.value)}
|
||||
placeholder={action === 'create' ? 'Auto-generated if empty' : 'Enter connection ID'}
|
||||
autoFocus={action === 'connect'}
|
||||
/>
|
||||
{action === 'create' && !connectionId && (
|
||||
<p className="help-text">Leave empty to auto-generate a random ID</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="button-row">
|
||||
<button className="back-button" onClick={onBack}>← Back</button>
|
||||
@@ -99,12 +35,10 @@ function ConnectionForm({
|
||||
onClick={onConnect}
|
||||
disabled={
|
||||
connectionStatus === 'connecting' ||
|
||||
(method === 'topic' && !topic) ||
|
||||
(method === 'peer-id' && (!topic || !peerId)) ||
|
||||
(method === 'connection-id' && action === 'join' && !connectionId)
|
||||
(action === 'connect' && !connectionId)
|
||||
}
|
||||
>
|
||||
{connectionStatus === 'connecting' ? 'Connecting...' : 'Connect'}
|
||||
{connectionStatus === 'connecting' ? 'Connecting...' : (action === 'create' ? 'Create' : 'Connect')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ function Header() {
|
||||
<header className="header">
|
||||
<div className="header-content">
|
||||
<h1>Rondevu</h1>
|
||||
<p className="tagline">Meet WebRTC peers by topic, peer ID, or connection ID</p>
|
||||
<p className="tagline">Simple WebRTC peer signaling and discovery. Meet peers by topic, peer ID, or connection ID.</p>
|
||||
<div className="header-links">
|
||||
<a href="https://github.com/xtr-dev/rondevu-client" target="_blank" rel="noopener noreferrer">
|
||||
<svg className="github-icon" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
|
||||
@@ -21,7 +21,7 @@ function Header() {
|
||||
<svg className="github-icon" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
|
||||
</svg>
|
||||
View source
|
||||
Demo
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
120
src/components/TopicsList.jsx
Normal file
120
src/components/TopicsList.jsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
function TopicsList({ rdv, onClose }) {
|
||||
const [topics, setTopics] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [page, setPage] = useState(1);
|
||||
const [pagination, setPagination] = useState(null);
|
||||
const [limit] = useState(20);
|
||||
|
||||
useEffect(() => {
|
||||
loadTopics();
|
||||
}, [page]);
|
||||
|
||||
const loadTopics = async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await rdv.api.listTopics(page, limit);
|
||||
setTopics(response.topics);
|
||||
setPagination(response.pagination);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRefresh = () => {
|
||||
loadTopics();
|
||||
};
|
||||
|
||||
const handlePrevPage = () => {
|
||||
if (page > 1) {
|
||||
setPage(page - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleNextPage = () => {
|
||||
if (pagination?.hasMore) {
|
||||
setPage(page + 1);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="modal-overlay" onClick={onClose}>
|
||||
<div className="modal-content topics-modal" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="modal-header">
|
||||
<h2>Active Topics</h2>
|
||||
<button className="close-button" onClick={onClose}>×</button>
|
||||
</div>
|
||||
|
||||
<div className="modal-body">
|
||||
{error && (
|
||||
<div className="error-message" style={{ marginBottom: '1rem' }}>
|
||||
Error: {error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{loading ? (
|
||||
<div className="loading-message">Loading topics...</div>
|
||||
) : (
|
||||
<>
|
||||
{topics.length === 0 ? (
|
||||
<div className="empty-message">
|
||||
No active topics found. Be the first to create one!
|
||||
</div>
|
||||
) : (
|
||||
<div className="topics-list">
|
||||
{topics.map((topic) => (
|
||||
<div key={topic.topic} className="topic-item">
|
||||
<div className="topic-name">{topic.topic}</div>
|
||||
<div className="topic-count">
|
||||
{topic.count} {topic.count === 1 ? 'peer' : 'peers'}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{pagination && (
|
||||
<div className="pagination">
|
||||
<button
|
||||
onClick={handlePrevPage}
|
||||
disabled={page === 1}
|
||||
className="pagination-button"
|
||||
>
|
||||
← Previous
|
||||
</button>
|
||||
<span className="pagination-info">
|
||||
Page {pagination.page} of {Math.ceil(pagination.total / pagination.limit)}
|
||||
{' '}({pagination.total} total)
|
||||
</span>
|
||||
<button
|
||||
onClick={handleNextPage}
|
||||
disabled={!pagination.hasMore}
|
||||
className="pagination-button"
|
||||
>
|
||||
Next →
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="modal-footer">
|
||||
<button onClick={handleRefresh} className="button button-secondary">
|
||||
🔄 Refresh
|
||||
</button>
|
||||
<button onClick={onClose} className="button button-primary">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default TopicsList;
|
||||
226
src/index.css
226
src/index.css
@@ -162,6 +162,12 @@ body {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
margin-top: 8px;
|
||||
font-size: 0.85rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
@@ -790,3 +796,223 @@ input[type="text"]:disabled {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Topics List Modal */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
animation: fadeIn 0.2s ease-out;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
max-width: 600px;
|
||||
width: 90%;
|
||||
max-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
|
||||
animation: slideUp 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 24px 32px;
|
||||
border-bottom: 1px solid #e8eaf0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.modal-header h2 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 2rem;
|
||||
color: #6c757d;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.close-button:hover {
|
||||
background: #f8f9fc;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 24px 32px;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 20px 32px;
|
||||
border-top: 1px solid #e8eaf0;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.topics-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.topic-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 20px;
|
||||
background: #f8f9fc;
|
||||
border: 1px solid #e8eaf0;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.topic-item:hover {
|
||||
background: #f0f2f8;
|
||||
border-color: #d0d5dd;
|
||||
}
|
||||
|
||||
.topic-name {
|
||||
font-weight: 500;
|
||||
color: #1a1a1a;
|
||||
font-size: 1rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.topic-count {
|
||||
font-size: 0.9rem;
|
||||
color: #6c757d;
|
||||
background: white;
|
||||
padding: 4px 12px;
|
||||
border-radius: 12px;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 24px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #e8eaf0;
|
||||
}
|
||||
|
||||
.pagination-button {
|
||||
padding: 10px 20px;
|
||||
border: 1px solid #e8eaf0;
|
||||
background: white;
|
||||
color: #1a1a1a;
|
||||
border-radius: 8px;
|
||||
font-size: 0.95rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.pagination-button:hover:not(:disabled) {
|
||||
background: #f8f9fc;
|
||||
border-color: #5568d3;
|
||||
color: #5568d3;
|
||||
}
|
||||
|
||||
.pagination-button:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.pagination-info {
|
||||
font-size: 0.9rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.loading-message, .empty-message, .error-message {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
color: #6c757d;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #dc3545;
|
||||
background: #ffe8eb;
|
||||
border: 1px solid #ffccd2;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.view-topics-button {
|
||||
position: fixed;
|
||||
bottom: 80px;
|
||||
right: 20px;
|
||||
padding: 14px 24px;
|
||||
background: #5568d3;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 12px rgba(85, 104, 211, 0.3);
|
||||
transition: all 0.2s;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.view-topics-button:hover {
|
||||
background: #667eea;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(85, 104, 211, 0.4);
|
||||
}
|
||||
|
||||
/* Topic cards grid for discovery */
|
||||
.topic-card-hover {
|
||||
padding: 25px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
border: 2px solid #e0e0e0;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.topic-card-hover:hover {
|
||||
border-color: #667eea;
|
||||
background: #fafbfc;
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user