feat(device): add Share tab with QR code and URI for sharing own contact

Adds a Share tab to the Device Info modal that generates a QR code
and copyable URI (meshcore://contact/add?...) for sharing the device
contact with other users.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
MarekWo
2026-03-25 09:12:30 +01:00
parent ce88ec291f
commit 2e6f0d01d6
2 changed files with 79 additions and 0 deletions

View File

@@ -1702,8 +1702,74 @@ async function loadDeviceStats() {
// Load stats when Stats tab is clicked
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('statsTabBtn')?.addEventListener('shown.bs.tab', loadDeviceStats);
document.getElementById('shareTabBtn')?.addEventListener('shown.bs.tab', loadDeviceShare);
});
/**
* Load device share tab - generate QR code and URI for sharing own contact
*/
async function loadDeviceShare() {
const container = document.getElementById('deviceShareContent');
if (!container) return;
container.innerHTML = '<div class="text-center py-3"><div class="spinner-border spinner-border-sm"></div> Loading...</div>';
try {
const response = await fetch('/api/device/info');
const data = await response.json();
if (!data.success) {
container.innerHTML = `<div class="alert alert-danger mb-0">${escapeHtml(data.error)}</div>`;
return;
}
const info = data.info;
if (!info || !info.public_key || !info.name) {
container.innerHTML = '<div class="alert alert-warning mb-0">Device info not available</div>';
return;
}
const contactType = info.adv_type || 1;
const uri = `meshcore://contact/add?name=${encodeURIComponent(info.name)}&public_key=${info.public_key}&type=${contactType}`;
const typeNames = { 1: 'Companion', 2: 'Repeater', 3: 'Room Server', 4: 'Sensor' };
let html = '<div class="text-center">';
html += '<p class="text-muted small mb-3">Share this QR code or URI so others can add your device as a contact.</p>';
html += '<div id="shareQrCode" class="d-inline-block mb-3"></div>';
html += '<div class="mb-2"><strong>' + escapeHtml(info.name) + '</strong></div>';
html += '<div class="text-muted small mb-3">' + escapeHtml(typeNames[contactType] || 'Unknown') + '</div>';
html += '</div>';
html += '<div class="mb-3">';
html += '<label class="form-label text-muted small">Contact URI:</label>';
html += '<div class="input-group">';
html += '<input type="text" class="form-control form-control-sm font-monospace" value="' + escapeHtml(uri) + '" readonly id="shareUriInput">';
html += '<button class="btn btn-outline-secondary btn-sm" onclick="copyToClipboard(document.getElementById(\'shareUriInput\').value, this)" title="Copy URI"><i class="bi bi-clipboard"></i></button>';
html += '</div>';
html += '</div>';
container.innerHTML = html;
// Generate QR code
const qrContainer = document.getElementById('shareQrCode');
if (qrContainer && typeof QRCode !== 'undefined') {
new QRCode(qrContainer, {
text: uri,
width: 200,
height: 200,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.M
});
}
} catch (error) {
console.error('Error loading device share:', error);
container.innerHTML = '<div class="alert alert-danger mb-0">Failed to load device info</div>';
}
}
// =============================================================================
// Settings Modal
// =============================================================================

View File

@@ -312,6 +312,9 @@
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#tabDeviceStats" type="button" id="statsTabBtn">Stats</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#tabDeviceShare" type="button" id="shareTabBtn">Share</button>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="tabDeviceInfo">
@@ -328,6 +331,13 @@
</div>
</div>
</div>
<div class="tab-pane fade" id="tabDeviceShare">
<div id="deviceShareContent">
<div class="text-center py-3 text-muted">
Click to generate share code
</div>
</div>
</div>
</div>
</div>
</div>
@@ -610,6 +620,9 @@
<script src="{{ url_for('static', filename='vendor/socket.io/socket.io.min.js') }}"></script>
<!-- Custom JS -->
<!-- QR Code generator (for Device Share) -->
<script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js"></script>
<script src="{{ url_for('static', filename='js/app.js') }}"></script>
<!-- PWA Viewport Fix for Android -->