From 2e3408e8d76117328d4dc54426a605e1fba9de65 Mon Sep 17 00:00:00 2001 From: ordinarthur <@arthurbarre.js@gmail.com> Date: Tue, 14 Apr 2026 12:34:01 +0200 Subject: [PATCH] fix: local IP detection regex + add join code UI when no peers found The ICE candidate regex was matching the wrong part of the candidate string. Also, iOS Safari blocks local IP detection via WebRTC (mDNS obfuscation), so when no peers are found, show a prominent "create link" button and a join-by-code input for easy cross-network pairing. Co-Authored-By: Claude Opus 4.6 --- web/src/components/PeerList.tsx | 58 ++++++++++++++++++++++++++++++--- web/src/lib/localIP.ts | 16 +++++---- web/src/pages/Home.tsx | 2 +- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/web/src/components/PeerList.tsx b/web/src/components/PeerList.tsx index eb04bf2..6a4be42 100644 --- a/web/src/components/PeerList.tsx +++ b/web/src/components/PeerList.tsx @@ -1,23 +1,73 @@ +import { useState } from "react"; import { useStore } from "../stores/useStore"; import PeerAvatar from "./PeerAvatar"; interface PeerListProps { onPeerSelect: (peerId: string) => void; + onCreateRoom?: () => void; } -export default function PeerList({ onPeerSelect }: PeerListProps) { +export default function PeerList({ onPeerSelect, onCreateRoom }: PeerListProps) { const peers = useStore((s) => s.peers); const selectedPeerId = useStore((s) => s.selectedPeerId); + const [joinCode, setJoinCode] = useState(""); if (peers.length === 0) { return ( -
+
📡

En attente d'appareils...

- Ouvrez AnyDrop sur un autre appareil connecté au même Wi-Fi, - ou partagez un lien public. + Ouvrez AnyDrop sur un autre appareil connecté au même Wi-Fi.

+ +
+

Pas sur le même réseau ?

+ + {onCreateRoom && ( + + )} + +
+ setJoinCode(e.target.value.toLowerCase().trim())} + placeholder="Code de partage" + maxLength={4} + className="flex-1 px-3 py-2.5 bg-slate-800/50 border border-slate-700 + rounded-xl text-white text-sm text-center font-mono tracking-widest + placeholder:text-slate-600 focus:outline-none focus:border-brand-500" + onKeyDown={(e) => { + if (e.key === "Enter" && joinCode.length >= 3) { + window.location.href = `/${joinCode}`; + } + }} + /> + +
+
); } diff --git a/web/src/lib/localIP.ts b/web/src/lib/localIP.ts index 32c83b1..a2f5362 100644 --- a/web/src/lib/localIP.ts +++ b/web/src/lib/localIP.ts @@ -26,13 +26,15 @@ export async function detectLocalIP(timeoutMs = 3000): Promise { if (!event.candidate?.candidate) return; - const match = event.candidate.candidate.match( - /(?:srflx|host)\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s/, - ); - if (match) { - const ip = match[1]; - if (isPrivateIPv4(ip)) { - done(ip); + // ICE candidate format: "candidate: typ ..." + // Extract all IPv4 addresses from the candidate string + const ips = event.candidate.candidate.match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g); + if (ips) { + for (const ip of ips) { + if (isPrivateIPv4(ip)) { + done(ip); + return; + } } } }; diff --git a/web/src/pages/Home.tsx b/web/src/pages/Home.tsx index a00bc87..142a1bb 100644 --- a/web/src/pages/Home.tsx +++ b/web/src/pages/Home.tsx @@ -124,7 +124,7 @@ function HomeConnected() { {/* Peer list */}
- +
{/* Drop zone */}