Skip to content

Commit 016fcd9

Browse files
authored
Merge pull request #22 from devanshu-puri/feature/peer-status
Feature/peer status
2 parents a989a20 + a54dc4d commit 016fcd9

File tree

5 files changed

+217
-51
lines changed

5 files changed

+217
-51
lines changed

dev-dist/sw.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,11 @@ define(['./workbox-9dc17825'], (function (workbox) { 'use strict';
8181
"url": "registerSW.js",
8282
"revision": "3ca0b8505b4bec776b69afdba2768812"
8383
}, {
84-
"url": "/offline.html",
85-
"revision": "0.kvp38gtfuk"
84+
"url": "index.html",
85+
"revision": "0.6f6mcaamqrg"
8686
}], {});
8787
workbox.cleanupOutdatedCaches();
88-
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("/offline.html"), {
88+
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
8989
allowlist: [/^\/$/],
9090
denylist: [/^\/api\//]
9191
}));

src/components/StatusSelector.jsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useState, useEffect } from "react";
2+
3+
// Define the available statuses
4+
const STATUS_OPTIONS = [
5+
{ label: "Online 🟢", value: "online" },
6+
{ label: "Away 🟠", value: "away" },
7+
{ label: "Busy 🔴", value: "busy" },
8+
{ label: "Offline ⚫", value: "offline" },
9+
];
10+
11+
export default function StatusSelector({ status: initialStatus, onChange }) {
12+
const [status, setStatus] = useState(initialStatus || "online");
13+
14+
// Keep local state & trigger callback
15+
useEffect(() => {
16+
if (onChange) onChange(status);
17+
}, [status]);
18+
19+
return (
20+
<select
21+
value={status}
22+
onChange={(e) => setStatus(e.target.value)}
23+
style={{
24+
padding: "6px 10px",
25+
borderRadius: "6px",
26+
fontWeight: 500,
27+
background: "#1e293b",
28+
color: "#e2e8f0",
29+
border: "1px solid #334155",
30+
}}
31+
>
32+
{STATUS_OPTIONS.map((opt) => (
33+
<option key={opt.value} value={opt.value}>
34+
{opt.label}
35+
</option>
36+
))}
37+
</select>
38+
);
39+
}

src/pages/App.jsx

Lines changed: 134 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React, { useEffect, useMemo, useRef, useState } from 'react'
22
import { Peer } from 'peerjs'
3+
import StatusSelector from "../components/StatusSelector";
4+
35

46
// Simple in-memory presence for same-tab demo. For multi-user on same Wi-Fi without a server,
57
// users need to share their Peer IDs manually or via a QR/text. We add a QR helper below.
@@ -27,6 +29,8 @@ const defaultConfig = {
2729
path: '/' // default path for public broker
2830
}
2931

32+
33+
3034
function copy(text) {
3135
navigator.clipboard?.writeText(text).catch(() => { })
3236
}
@@ -46,7 +50,11 @@ export default function App() {
4650
const [cfg, setCfg] = useLocalStorage('peer.cfg', defaultConfig)
4751
const [label, setLabel] = useLocalStorage('peer.label', '')
4852
const [recentRooms, setRecentRooms] = useLocalStorage('peer.recentRooms', [])
49-
const [status, setStatus] = useState('idle')
53+
// replacing const [status, setStatus] = useState('idle')
54+
55+
const [status, setStatus] = useState(localStorage.getItem("peer.status") || 'idle');
56+
//Add a peerStatuses state object:
57+
const [peerStatuses, setPeerStatuses] = useState({});
5058
const [myId, setMyId] = useState('')
5159
const [peerIdInput, setPeerIdInput] = useState('')
5260
const [roomCode, setRoomCode] = useState('')
@@ -61,11 +69,23 @@ export default function App() {
6169
const [showRecentRooms, setShowRecentRooms] = useState(false)
6270

6371
const peerRef = useRef(null)
64-
const connRef = useRef(null)
72+
const connRef = useRef({})
6573
const mediaRef = useRef(null)
6674
const audioRef = useRef(null)
6775
const chatEndRef = useRef(null)
6876

77+
78+
//adding function for better optimization
79+
function getStatusColor(status) {
80+
switch(status) {
81+
case 'online': return '#10b981';
82+
case 'away': return '#f59e0b';
83+
case 'busy': return '#ef4444';
84+
case 'offline':return '#6b7280';
85+
default: return '#10b981';
86+
}
87+
}
88+
6989
const peerOptions = useMemo(() => ({
7090
host: cfg.host, port: Number(cfg.port), secure: !!cfg.secure, path: cfg.path || '/',
7191
config: {
@@ -145,21 +165,49 @@ export default function App() {
145165
setConnected(true)
146166
pushLog('Connected to ' + conn.peer)
147167

148-
conn.on('data', (data) => {
149-
if (data?.type === 'presence') {
150-
setPeers((p) => mergePeers(p, data.payload))
151-
} else if (data?.type === 'message') {
152-
setMessages((msgs) => [...msgs, {
168+
conn.on('data', (data) => {
169+
if (!data) return;
170+
171+
switch (data.type) {
172+
case 'presence':
173+
// Merge presence info into peers list
174+
setPeers((prevPeers) => mergePeers(prevPeers, data.payload));
175+
break;
176+
177+
case 'message':
178+
// Append incoming chat message
179+
setMessages((msgs) => [
180+
...msgs,
181+
{
153182
text: data.text,
154183
sender: data.sender,
155184
timestamp: data.timestamp,
156-
isMe: false
157-
}])
158-
pushLog(`Message from ${data.sender}: ${data.text}`)
159-
} else if (data?.type === 'signal') {
160-
// Place for extra messages if needed
161-
}
162-
})
185+
isMe: false,
186+
},
187+
]);
188+
pushLog(`Message from ${data.sender}: ${data.text}`);
189+
break;
190+
191+
case 'status-update':
192+
// Update peerStatuses map with latest status
193+
setPeerStatuses((prev) => ({
194+
...prev,
195+
[conn.peer]: data.status,
196+
}));
197+
pushLog(`Status update from ${conn.peer}: ${data.status}`);
198+
break;
199+
200+
case 'signal':
201+
// Reserved for future signals
202+
break;
203+
204+
default:
205+
console.warn('Unknown data type received:', data);
206+
break;
207+
}
208+
});
209+
210+
163211

164212
conn.on('close', () => {
165213
setConnected(false)
@@ -301,6 +349,7 @@ export default function App() {
301349
const qrUrl = myId ? generateQR(shareableUrl) : ''
302350

303351
return (
352+
304353
<div className="app">
305354
<style>{`
306355
.app {
@@ -505,6 +554,30 @@ export default function App() {
505554
}
506555
`}</style>
507556

557+
{/* availability Status Section */}
558+
<div className="card" style={{ marginTop: 16, padding: 16 }}>
559+
<div className="small" style={{ marginBottom: 8 }}>Your Availability</div>
560+
<StatusSelector
561+
status={status}
562+
onChange={(newStatus) => {
563+
setStatus(newStatus);
564+
localStorage.setItem("peer.status", newStatus);
565+
566+
// Broadcast to all connected peers
567+
peers.forEach((p) => {
568+
const conn = connRef.current[p.id];
569+
if (conn && conn.open) {
570+
conn.send({ type: "status-update", status: newStatus });
571+
}
572+
});
573+
}}
574+
/>
575+
576+
</div>
577+
578+
579+
580+
508581
<div className="card">
509582
<div className="header">
510583
<h2 className="h">Local P2P Voice Chat</h2>
@@ -675,36 +748,53 @@ export default function App() {
675748
</div>
676749

677750
{/* Presence Display Section */}
678-
<div className="card" style={{ marginTop: 16, padding: 16 }}>
679-
<div className="small" style={{ marginBottom: 8 }}>Connected Peers</div>
680-
{peers.length === 0 ? (
681-
<div className="small" style={{ opacity: 0.5, textAlign: 'center', padding: '20px' }}>
682-
No peers connected. Share your Peer ID or room code to connect with others.
683-
</div>
684-
) : (
685-
<div className="list">
686-
{peers.map((peer, i) => (
687-
<div key={i} className="peer">
688-
<div>
689-
<div style={{ fontWeight: '500' }}>
690-
{peer.label || 'Anonymous'}
691-
{peer.roomCode && <span className="badge small" style={{ marginLeft: '8px' }}>Room: {peer.roomCode}</span>}
692-
</div>
693-
<div className="small" style={{ fontFamily: 'monospace' }}>
694-
{peer.id}
695-
</div>
696-
<div className="small" style={{ opacity: 0.7 }}>
697-
Last seen: {new Date(peer.ts).toLocaleTimeString()}
698-
</div>
699-
</div>
700-
<div className="badge small" style={{ backgroundColor: '#10b981' }}>
701-
Online
702-
</div>
703-
</div>
704-
))}
705-
</div>
706-
)}
707-
</div>
751+
<div className="card" style={{ marginTop: 16, padding: 16 }}>
752+
<div className="small" style={{ marginBottom: 8 }}>Connected Peers</div>
753+
{peers.length === 0 ? (
754+
<div className="small" style={{ opacity: 0.5, textAlign: 'center', padding: '20px' }}>
755+
No peers connected. Share your Peer ID or room code to connect with others.
756+
</div>
757+
) : (
758+
<div className="list">
759+
{peers.map((peer, i) => (
760+
<div key={i} className="peer" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
761+
<div>
762+
<div style={{ fontWeight: 500 }}>
763+
{peer.label || 'Anonymous'}
764+
{peer.roomCode && (
765+
<span className="badge small" style={{ marginLeft: '8px', backgroundColor: '#3b82f6' }}>
766+
Room: {peer.roomCode}
767+
</span>
768+
)}
769+
</div>
770+
<div className="small" style={{ fontFamily: 'monospace' }}>
771+
{peer.id}
772+
</div>
773+
<div className="small" style={{ opacity: 0.7 }}>
774+
Last seen: {new Date(peer.ts).toLocaleTimeString()}
775+
</div>
776+
</div>
777+
778+
{/* Dynamic Status Badge */}
779+
<div
780+
className="badge small"
781+
style={{
782+
backgroundColor: getStatusColor(peerStatuses[peer.id]),
783+
color: 'white',
784+
padding: '2px 8px',
785+
borderRadius: '6px',
786+
fontWeight: 500,
787+
}}
788+
>
789+
{peerStatuses[peer.id]?.toUpperCase() || 'ONLINE'}
790+
</div>
791+
792+
</div>
793+
))}
794+
</div>
795+
)}
796+
</div>
797+
708798

709799
<div className="card" style={{ marginTop: 16, padding: 16 }}>
710800
<div className="small" style={{ marginBottom: 8 }}>Log</div>

src/styles.css

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,35 @@ button:hover:not(:disabled) {
133133
audio {
134134
width: 100%;
135135
}
136+
137+
/* adding style */
138+
.status-selector {
139+
margin: 10px 0;
140+
font-weight: 500;
141+
}
142+
143+
.status-dot {
144+
text-transform: capitalize;
145+
padding: 3px 8px;
146+
border-radius: 8px;
147+
}
148+
149+
.status-dot.online {
150+
background: #2ecc71;
151+
color: white;
152+
}
153+
154+
.status-dot.away {
155+
background: #f1c40f;
156+
color: black;
157+
}
158+
159+
.status-dot.busy {
160+
background: #e74c3c;
161+
color: white;
162+
}
163+
164+
.status-dot.offline {
165+
background: #7f8c8d;
166+
color: white;
167+
}

vite.config.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,15 @@ export default defineConfig({
3131
registerType: 'autoUpdate',
3232
workbox: {
3333
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
34-
navigateFallback: '/offline.html',
35-
navigateFallbackDenylist: [/^\\/api\\//],
34+
// navigateFallback: '/offline.html',
35+
36+
// Fixed regex: only single backslashes needed in literal
37+
navigateFallbackDenylist: [/^\/api\//],
38+
3639
runtimeCaching: [
3740
{
38-
urlPattern: /^https:\\/\\/0\\.peerjs\\.com\\//,
41+
// Fixed regex: escape dots properly, not slashes
42+
urlPattern: /^https:\/\/0\.peerjs\.com\//,
3943
handler: 'NetworkFirst',
4044
options: {
4145
cacheName: 'peerjs-cache',
@@ -46,7 +50,8 @@ export default defineConfig({
4650
}
4751
},
4852
{
49-
urlPattern: /^https:\\/\\/api\\.qrserver\\.com\\//,
53+
// Fixed regex: same correction here
54+
urlPattern: /^https:\/\/api\.qrserver\.com\//,
5055
handler: 'CacheFirst',
5156
options: {
5257
cacheName: 'qr-cache',

0 commit comments

Comments
 (0)