feat(peer-discovery): implement global peer discovery and update peer profile handling
This commit is contained in:
parent
34cc1883f5
commit
b4f75cd995
|
|
@ -157,6 +157,16 @@ pub async fn start_node(app: tauri::AppHandle, state: State<'_, AppState>) -> Re
|
|||
namespace: personal_ns,
|
||||
})
|
||||
.await;
|
||||
|
||||
// register under the global "dusk/peers" namespace so any peer can
|
||||
// discover us via the relay tracker, enabling global peer discovery
|
||||
// without exposing ip addresses (all connections use relay circuit)
|
||||
let _ = handle
|
||||
.command_tx
|
||||
.send(NodeCommand::RegisterRendezvous {
|
||||
namespace: "dusk/peers".to_string(),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -258,6 +258,22 @@ pub async fn remove_friend(state: State<'_, AppState>, peer_id: String) -> Resul
|
|||
.map_err(|e| format!("failed to remove friend: {}", e))
|
||||
}
|
||||
|
||||
// discover online peers via the global relay tracker namespace
|
||||
// this allows finding peers without sharing a community or knowing their peer_id
|
||||
#[tauri::command]
|
||||
pub async fn discover_global_peers(state: State<'_, AppState>) -> Result<(), String> {
|
||||
let node_handle = state.node_handle.lock().await;
|
||||
if let Some(ref handle) = *node_handle {
|
||||
let _ = handle
|
||||
.command_tx
|
||||
.send(crate::node::NodeCommand::DiscoverRendezvous {
|
||||
namespace: "dusk/peers".to_string(),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// broadcast a revocation to all peers, stop the node, and wipe all local data
|
||||
#[tauri::command]
|
||||
pub async fn reset_identity(state: State<'_, AppState>) -> Result<(), String> {
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ pub fn run() {
|
|||
commands::identity::get_friends,
|
||||
commands::identity::add_friend,
|
||||
commands::identity::remove_friend,
|
||||
commands::identity::discover_global_peers,
|
||||
commands::identity::reset_identity,
|
||||
commands::chat::send_message,
|
||||
commands::chat::get_messages,
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ pub enum DuskEvent {
|
|||
peer_id: String,
|
||||
display_name: String,
|
||||
bio: String,
|
||||
public_key: String,
|
||||
},
|
||||
#[serde(rename = "profile_revoked")]
|
||||
ProfileRevoked { peer_id: String },
|
||||
|
|
@ -396,6 +397,7 @@ pub async fn start(
|
|||
peer_id: profile.peer_id,
|
||||
display_name: profile.display_name,
|
||||
bio: profile.bio,
|
||||
public_key: profile.public_key,
|
||||
});
|
||||
}
|
||||
crate::protocol::messages::GossipMessage::ProfileRevoke(revocation) => {
|
||||
|
|
|
|||
|
|
@ -342,6 +342,7 @@ const App: Component = () => {
|
|||
event.payload.peer_id,
|
||||
event.payload.display_name,
|
||||
event.payload.bio,
|
||||
event.payload.public_key,
|
||||
);
|
||||
// keep dm conversation names in sync
|
||||
updateDMPeerDisplayName(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
import { Component, createSignal, createMemo, For, Show } from "solid-js";
|
||||
import {
|
||||
Component,
|
||||
createSignal,
|
||||
createMemo,
|
||||
createEffect,
|
||||
For,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import { Portal } from "solid-js/web";
|
||||
import {
|
||||
X,
|
||||
|
|
@ -34,6 +41,17 @@ const UserDirectoryModal: Component<UserDirectoryModalProps> = (props) => {
|
|||
const [activeTab, setActiveTab] = createSignal<DirectoryTab>("all");
|
||||
const [copiedId, setCopiedId] = createSignal<string | null>(null);
|
||||
|
||||
// trigger global peer discovery when modal opens
|
||||
createEffect(() => {
|
||||
if (props.isOpen) {
|
||||
// discover peers registered on the relay's global "dusk/peers" namespace
|
||||
// this allows finding online peers without sharing a community
|
||||
tauri.discoverGlobalPeers().catch((e) => {
|
||||
console.error("failed to discover global peers:", e);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// filter out our own peer id from the directory
|
||||
const filteredPeers = createMemo(() => {
|
||||
const myId = identity()?.peer_id;
|
||||
|
|
|
|||
|
|
@ -202,6 +202,10 @@ export async function removeFriend(peerId: string): Promise<void> {
|
|||
return invoke("remove_friend", { peerId });
|
||||
}
|
||||
|
||||
export async function discoverGlobalPeers(): Promise<void> {
|
||||
return invoke("discover_global_peers");
|
||||
}
|
||||
|
||||
export async function resetIdentity(): Promise<void> {
|
||||
return invoke("reset_identity");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,7 +174,12 @@ export type DuskEvent =
|
|||
| { kind: "sync_complete"; payload: { community_id: string } }
|
||||
| {
|
||||
kind: "profile_received";
|
||||
payload: { peer_id: string; display_name: string; bio: string };
|
||||
payload: {
|
||||
peer_id: string;
|
||||
display_name: string;
|
||||
bio: string;
|
||||
public_key: string;
|
||||
};
|
||||
}
|
||||
| { kind: "profile_revoked"; payload: { peer_id: string } }
|
||||
| { kind: "relay_status"; payload: { connected: boolean } }
|
||||
|
|
|
|||
|
|
@ -21,17 +21,34 @@ export function updatePeerProfile(
|
|||
peerId: string,
|
||||
displayName: string,
|
||||
bio: string,
|
||||
publicKey: string,
|
||||
) {
|
||||
const now = Date.now();
|
||||
|
||||
setKnownPeers((prev) =>
|
||||
prev.map((p) =>
|
||||
setKnownPeers((prev) => {
|
||||
const existing = prev.find((p) => p.peer_id === peerId);
|
||||
if (existing) {
|
||||
// update existing peer
|
||||
return prev.map((p) =>
|
||||
p.peer_id === peerId
|
||||
? { ...p, display_name: displayName, bio, last_seen: now }
|
||||
: p,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// add new peer that just announced themselves
|
||||
const newEntry: DirectoryEntry = {
|
||||
peer_id: peerId,
|
||||
display_name: displayName,
|
||||
bio,
|
||||
public_key: publicKey,
|
||||
last_seen: now,
|
||||
is_friend: false,
|
||||
};
|
||||
return [...prev, newEntry];
|
||||
}
|
||||
});
|
||||
|
||||
// update friends list if this peer is a friend
|
||||
setFriends((prev) =>
|
||||
prev.map((p) =>
|
||||
p.peer_id === peerId
|
||||
|
|
|
|||
Loading…
Reference in New Issue