feat(directory): update peer profile to include public key and improve placeholder handling in user directory
This commit is contained in:
parent
718bd9557e
commit
42f3f86f08
|
|
@ -730,8 +730,46 @@ pub async fn start(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// never expose relay infrastructure in the user directory
|
||||||
|
if Some(discovered_peer) == relay_peer {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
log::info!("discovered peer {} via rendezvous", discovered_peer);
|
log::info!("discovered peer {} via rendezvous", discovered_peer);
|
||||||
|
|
||||||
|
// cache a placeholder entry so global discovery is visible
|
||||||
|
// before we receive the peer's signed profile announcement
|
||||||
|
let now = std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_millis() as u64;
|
||||||
|
let discovered_peer_str = discovered_peer.to_string();
|
||||||
|
let already_known = storage
|
||||||
|
.load_directory()
|
||||||
|
.ok()
|
||||||
|
.map(|d| d.contains_key(&discovered_peer_str))
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
// add a lightweight placeholder if we have not learned this peer's profile yet
|
||||||
|
if !already_known {
|
||||||
|
let placeholder = DirectoryEntry {
|
||||||
|
peer_id: discovered_peer_str.clone(),
|
||||||
|
display_name: "discovered peer".to_string(),
|
||||||
|
bio: String::new(),
|
||||||
|
public_key: String::new(),
|
||||||
|
last_seen: now,
|
||||||
|
is_friend: false,
|
||||||
|
};
|
||||||
|
let _ = storage.save_directory_entry(&placeholder);
|
||||||
|
|
||||||
|
let _ = app_handle.emit("dusk-event", DuskEvent::ProfileReceived {
|
||||||
|
peer_id: placeholder.peer_id,
|
||||||
|
display_name: placeholder.display_name,
|
||||||
|
bio: placeholder.bio,
|
||||||
|
public_key: placeholder.public_key,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// connect through the relay circuit so neither peer reveals their IP
|
// connect through the relay circuit so neither peer reveals their IP
|
||||||
if let Some(ref relay_addr) = relay_multiaddr {
|
if let Some(ref relay_addr) = relay_multiaddr {
|
||||||
let circuit_addr = relay_addr.clone()
|
let circuit_addr = relay_addr.clone()
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import {
|
||||||
createSignal,
|
createSignal,
|
||||||
createMemo,
|
createMemo,
|
||||||
createEffect,
|
createEffect,
|
||||||
|
onCleanup,
|
||||||
For,
|
For,
|
||||||
Show,
|
Show,
|
||||||
} from "solid-js";
|
} from "solid-js";
|
||||||
|
|
@ -29,6 +30,7 @@ import { identity } from "../../stores/identity";
|
||||||
import { setActiveDM } from "../../stores/dms";
|
import { setActiveDM } from "../../stores/dms";
|
||||||
import { addDMConversation } from "../../stores/dms";
|
import { addDMConversation } from "../../stores/dms";
|
||||||
import * as tauri from "../../lib/tauri";
|
import * as tauri from "../../lib/tauri";
|
||||||
|
import type { DirectoryEntry } from "../../lib/types";
|
||||||
|
|
||||||
interface UserDirectoryModalProps {
|
interface UserDirectoryModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
|
@ -41,10 +43,18 @@ const UserDirectoryModal: Component<UserDirectoryModalProps> = (props) => {
|
||||||
const [searchQuery, setSearchQuery] = createSignal("");
|
const [searchQuery, setSearchQuery] = createSignal("");
|
||||||
const [activeTab, setActiveTab] = createSignal<DirectoryTab>("all");
|
const [activeTab, setActiveTab] = createSignal<DirectoryTab>("all");
|
||||||
const [copiedId, setCopiedId] = createSignal<string | null>(null);
|
const [copiedId, setCopiedId] = createSignal<string | null>(null);
|
||||||
|
const [isSearching, setIsSearching] = createSignal(false);
|
||||||
|
const [searchResults, setSearchResults] = createSignal<DirectoryEntry[] | null>(
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
// reload directory from disk and trigger fresh discovery when modal opens
|
// reload directory from disk and trigger fresh discovery when modal opens
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (props.isOpen) {
|
if (props.isOpen) {
|
||||||
|
setSearchQuery("");
|
||||||
|
setIsSearching(false);
|
||||||
|
setSearchResults(null);
|
||||||
|
|
||||||
// refresh the in-memory peer list from disk so any profiles received
|
// refresh the in-memory peer list from disk so any profiles received
|
||||||
// while the modal was closed are visible immediately
|
// while the modal was closed are visible immediately
|
||||||
tauri.getKnownPeers().then((peers) => {
|
tauri.getKnownPeers().then((peers) => {
|
||||||
|
|
@ -59,13 +69,46 @@ const UserDirectoryModal: Component<UserDirectoryModalProps> = (props) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (!props.isOpen) return;
|
||||||
|
|
||||||
|
const query = searchQuery().trim();
|
||||||
|
if (!query) {
|
||||||
|
setIsSearching(false);
|
||||||
|
setSearchResults(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cancelled = false;
|
||||||
|
setIsSearching(true);
|
||||||
|
const searchTimeout = window.setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
const results = await tauri.searchDirectory(query);
|
||||||
|
if (!cancelled) {
|
||||||
|
setSearchResults(results);
|
||||||
|
setIsSearching(false);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
if (!cancelled) {
|
||||||
|
setIsSearching(false);
|
||||||
|
setSearchResults(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 180);
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
cancelled = true;
|
||||||
|
window.clearTimeout(searchTimeout);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// filter out our own peer id from the directory
|
// filter out our own peer id from the directory
|
||||||
const filteredPeers = createMemo(() => {
|
const filteredPeers = createMemo(() => {
|
||||||
const myId = identity()?.peer_id;
|
const myId = identity()?.peer_id;
|
||||||
const query = searchQuery().toLowerCase().trim();
|
const query = searchQuery().toLowerCase().trim();
|
||||||
const tab = activeTab();
|
const tab = activeTab();
|
||||||
|
|
||||||
let peers = knownPeers();
|
let peers = (searchResults() ?? knownPeers()).filter((p) => p.peer_id !== myId);
|
||||||
|
|
||||||
if (tab === "friends") {
|
if (tab === "friends") {
|
||||||
peers = peers.filter((p) => p.is_friend);
|
peers = peers.filter((p) => p.is_friend);
|
||||||
|
|
@ -77,9 +120,6 @@ const UserDirectoryModal: Component<UserDirectoryModalProps> = (props) => {
|
||||||
p.display_name.toLowerCase().includes(query) ||
|
p.display_name.toLowerCase().includes(query) ||
|
||||||
p.peer_id.toLowerCase().includes(query),
|
p.peer_id.toLowerCase().includes(query),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
// if not searching, hide self from the list to avoid confusion
|
|
||||||
peers = peers.filter((p) => p.peer_id !== myId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return peers;
|
return peers;
|
||||||
|
|
@ -195,6 +235,11 @@ const UserDirectoryModal: Component<UserDirectoryModalProps> = (props) => {
|
||||||
onInput={(e) => setSearchQuery(e.currentTarget.value)}
|
onInput={(e) => setSearchQuery(e.currentTarget.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<Show when={isSearching() && searchQuery().trim().length > 0}>
|
||||||
|
<p class="mt-2 text-[11px] font-mono text-white/35">
|
||||||
|
searching directory...
|
||||||
|
</p>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider class="mx-6" />
|
<Divider class="mx-6" />
|
||||||
|
|
@ -207,8 +252,10 @@ const UserDirectoryModal: Component<UserDirectoryModalProps> = (props) => {
|
||||||
<div class="flex flex-col items-center justify-center py-16">
|
<div class="flex flex-col items-center justify-center py-16">
|
||||||
<Users size={48} class="text-white/10 mb-4" />
|
<Users size={48} class="text-white/10 mb-4" />
|
||||||
<p class="text-[16px] text-white/30 mb-1">
|
<p class="text-[16px] text-white/30 mb-1">
|
||||||
{searchQuery()
|
{searchQuery().trim().length > 0
|
||||||
? "no peers matching your search"
|
? isSearching()
|
||||||
|
? "searching directory"
|
||||||
|
: "no peers matching your search"
|
||||||
: activeTab() === "friends"
|
: activeTab() === "friends"
|
||||||
? "no friends added yet"
|
? "no friends added yet"
|
||||||
: "no peers discovered yet"}
|
: "no peers discovered yet"}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,13 @@ export function updatePeerProfile(
|
||||||
// update existing peer
|
// update existing peer
|
||||||
return prev.map((p) =>
|
return prev.map((p) =>
|
||||||
p.peer_id === peerId
|
p.peer_id === peerId
|
||||||
? { ...p, display_name: displayName, bio, last_seen: now }
|
? {
|
||||||
|
...p,
|
||||||
|
display_name: displayName,
|
||||||
|
bio,
|
||||||
|
public_key: publicKey || p.public_key,
|
||||||
|
last_seen: now,
|
||||||
|
}
|
||||||
: p,
|
: p,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue