From b6dc03dce514be63c75470e83e928eb8be81dd49 Mon Sep 17 00:00:00 2001 From: MarekWo Date: Sun, 22 Mar 2026 21:38:41 +0100 Subject: [PATCH] feat(paths): add Name/ID search mode toggle in repeater picker ID mode searches by first N hex chars of public key (2/4/6 chars depending on selected hash size). Placeholder updates dynamically. Co-Authored-By: Claude Opus 4.6 --- app/static/js/dm.js | 55 +++++++++++++++++++++++++++++++++---------- app/templates/dm.html | 14 ++++++++--- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/app/static/js/dm.js b/app/static/js/dm.js index 66880c5..9eb511c 100644 --- a/app/static/js/dm.js +++ b/app/static/js/dm.js @@ -1841,7 +1841,7 @@ function setupPathFormHandlers(pubkey) { const newSearch = searchInput.cloneNode(true); searchInput.parentNode.replaceChild(newSearch, searchInput); newSearch.addEventListener('input', () => { - filterRepeaterList(newSearch.value); + filterRepeaterList(); }); } @@ -1894,15 +1894,27 @@ async function loadRepeaterPicker(pubkey) { renderRepeaterList(listEl, _repeatersCache, pubkey); } +function getRepeaterSearchMode() { + const checked = document.querySelector('input[name="repeaterSearchMode"]:checked'); + return checked ? checked.value : 'name'; +} + function renderRepeaterList(listEl, repeaters, pubkey) { const hashSize = parseInt(document.querySelector('input[name="pathHashSize"]:checked').value); const hexInput = document.getElementById('dmPathHexInput'); - const searchVal = (document.getElementById('dmRepeaterSearch')?.value || '').toLowerCase(); + const searchVal = (document.getElementById('dmRepeaterSearch')?.value || '').toLowerCase().trim(); + const searchMode = getRepeaterSearchMode(); + const prefixLen = hashSize * 2; // hex chars to match for ID mode - const filtered = repeaters.filter(r => - r.name.toLowerCase().includes(searchVal) || - r.public_key.toLowerCase().includes(searchVal) - ); + const filtered = repeaters.filter(r => { + if (!searchVal) return true; + if (searchMode === 'id') { + // Match only against the first prefixLen hex chars of public_key + const idPrefix = r.public_key.substring(0, prefixLen).toLowerCase(); + return idPrefix.startsWith(searchVal.substring(0, prefixLen)); + } + return r.name.toLowerCase().includes(searchVal); + }); if (!filtered.length) { listEl.innerHTML = '
No repeaters found
'; @@ -1943,15 +1955,13 @@ function renderRepeaterList(listEl, repeaters, pubkey) { }); } -function filterRepeaterList(searchVal) { +function filterRepeaterList() { if (!_repeatersCache) return; const listEl = document.getElementById('dmRepeaterList'); const pubkey = getCurrentContactPubkey(); if (listEl) { - renderRepeaterList(listEl, _repeatersCache.filter(r => - r.name.toLowerCase().includes(searchVal.toLowerCase()) || - r.public_key.toLowerCase().includes(searchVal.toLowerCase()) - ), pubkey); + // Filtering is done inside renderRepeaterList based on search input + mode + renderRepeaterList(listEl, _repeatersCache, pubkey); } } @@ -2025,7 +2035,7 @@ async function loadNoAutoFloodToggle(pubkey) { }); } -// Listen for hash size radio changes to re-render repeater list +// Listen for hash size and search mode radio changes document.addEventListener('change', (e) => { if (e.target.name === 'pathHashSize') { _repeatersCache = null; // Refresh to recalculate prefixes @@ -2036,5 +2046,26 @@ document.addEventListener('change', (e) => { // Clear path hex input when changing hash size const hexInput = document.getElementById('dmPathHexInput'); if (hexInput) hexInput.value = ''; + // Update ID search placeholder with new prefix length + updateRepeaterSearchPlaceholder(); + } + if (e.target.name === 'repeaterSearchMode') { + updateRepeaterSearchPlaceholder(); + const searchInput = document.getElementById('dmRepeaterSearch'); + if (searchInput) searchInput.value = ''; + filterRepeaterList(); } }); + +function updateRepeaterSearchPlaceholder() { + const searchInput = document.getElementById('dmRepeaterSearch'); + if (!searchInput) return; + const mode = getRepeaterSearchMode(); + if (mode === 'id') { + const hashSize = parseInt(document.querySelector('input[name="pathHashSize"]:checked')?.value || '1'); + const chars = hashSize * 2; + searchInput.placeholder = `Search by first ${chars} hex chars...`; + } else { + searchInput.placeholder = 'Search by name...'; + } +} diff --git a/app/templates/dm.html b/app/templates/dm.html index 91104ac..b0acec3 100644 --- a/app/templates/dm.html +++ b/app/templates/dm.html @@ -343,9 +343,17 @@ -