Our Info panel, hole punch race fix, NAT profiles in relay introduction

- Network Diagnostics: "Our Info" button shows addresses with NAT status,
  device role, UPnP, HTTP capability. Addresses stacked for mobile.
- Hole punch race: re-check for existing connection before and after relay
  introduction to avoid wasting minutes on redundant punch attempts.
- Relay introduction now carries requester/target NAT mapping+filtering
  so hole punch strategy uses fresh profiles instead of stale stored ones.
  Critical for phones that switch between WiFi/cellular/VPN.
- STUN fix: filter DNS results to IPv4 (was resolving to IPv6 first on
  dual-stack, causing silent send failure and "NAT unknown").
- Welcome screen: Ready button with loading bar for instant feed access.
- LAN addresses show just "LAN" (no misleading punchability label).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Scott Reimers 2026-04-05 17:57:41 -04:00
parent e354ccc388
commit be253e8001
7 changed files with 266 additions and 9 deletions

View file

@ -2986,9 +2986,14 @@ function openDiagnostics() {
<button id="request-referrals-btn" class="btn btn-ghost btn-sm">Request Referrals</button>
</div>
<div style="display:flex;gap:0.5rem;margin-top:0.5rem;flex-wrap:wrap;justify-content:center">
<button id="show-ourinfo-btn" class="btn btn-ghost btn-sm">Our Info</button>
<button id="show-connections-btn" class="btn btn-ghost btn-sm">Show Connections</button>
<button id="show-anchors-btn" class="btn btn-ghost btn-sm">Stored Anchors</button>
</div>
<div id="ourinfo-section" class="hidden">
<h4 class="subsection-title">Our Info</h4>
<div id="ourinfo-content"></div>
</div>
<div id="connections-section" class="hidden">
<h4 class="subsection-title">Mesh &amp; Session Connections</h4>
<div id="connections-list"></div>
@ -3006,6 +3011,45 @@ function openDiagnostics() {
networkSummaryEl = $('#network-summary');
connectionsList = $('#connections-list');
peersList = null; // Known peers removed
// Wire Our Info toggle
$('#show-ourinfo-btn').addEventListener('click', async () => {
const section = $('#ourinfo-section');
const btn = $('#show-ourinfo-btn');
if (section.classList.contains('hidden')) {
section.classList.remove('hidden');
btn.textContent = 'Hide Our Info';
try {
const info = await invoke('get_our_info');
const httpVal = info.httpAddr || 'No';
let html = `<div class="diag-grid" style="margin-bottom:0.5rem">
<div class="diag-item"><span class="diag-label">NAT Type</span><span class="diag-value" style="font-size:0.85rem">${info.natType}</span></div>
<div class="diag-item"><span class="diag-label">Role</span><span class="diag-value" style="font-size:0.85rem">${info.deviceRole}</span></div>
<div class="diag-item"><span class="diag-label">UPnP</span><span class="diag-value" style="font-size:0.85rem">${info.upnp ? 'Yes' : 'No'}</span></div>
</div>
<div style="text-align:center;margin-bottom:0.75rem">
<span style="color:#888;font-size:0.75rem">HTTP</span>
<span style="color:#ccc;font-size:0.8rem;margin-left:0.5rem">${httpVal}</span>
</div>`;
html += '<div style="font-size:0.8rem">';
for (const a of info.addresses) {
const color = a.status.includes('Public') || a.status.includes('punchable') || a.status.includes('Server') ? '#7fdbca' :
a.status.includes('UPnP') || a.status.includes('External') ? '#5b8def' : '#888';
html += `<div style="padding:0.3rem 0;border-bottom:1px solid #1a1a2e">
<div style="color:#ccc;font-family:monospace;font-size:0.7rem;word-break:break-all">${a.addr}</div>
<div style="color:${color};font-size:0.7rem;margin-top:0.1rem">${a.family} · ${a.status}</div>
</div>`;
}
html += '</div>';
html += `<p style="color:#555;font-size:0.7rem;text-align:center;margin-top:0.5rem;word-break:break-all">Node: ${info.nodeId.substring(0, 16)}…</p>`;
$('#ourinfo-content').innerHTML = html;
} catch (e) {
$('#ourinfo-content').innerHTML = `<p class="empty-hint">Failed to load: ${e}</p>`;
}
} else {
section.classList.add('hidden');
btn.textContent = 'Our Info';
}
});
// Wire connections toggle
$('#show-connections-btn').addEventListener('click', () => {
const section = $('#connections-section');