Feed pagination, duplicate identity detection, pkarr leak fix, Android SAF
Feed pagination: - Cursor-based pagination: get_feed_page/get_all_posts_page (20 posts/page) - Batched engagement queries (3 bulk SQL queries instead of 4 per post) - IntersectionObserver for infinite scroll (sentinel at midpoint) - Viewport-based media loading (blobs only load when post enters view) - Pre-fetch next page immediately after current page renders Duplicate identity detection: - Anchor detects when a NodeId is already mesh-connected during initial exchange and sets duplicate_active flag in response - Client skips sync tasks when duplicate detected - Frontend shows red warning banner Privacy: - Fixed pkarr leak: clear_address_lookup() removes default dns.iroh.link publishing. Only mDNS (local network) discovery enabled. Android: - SAF integration via tauri-plugin-android-fs: exports open native "Save As" dialog so users can save to Downloads/Drive/etc. - Download/export paths use app data dir on Android (writable) - File picker gated behind desktop cfg (blocking_pick not on Android) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5e7eed9638
commit
288b53ffb1
12 changed files with 910 additions and 120 deletions
|
|
@ -46,6 +46,8 @@ pub struct Network {
|
|||
bind_addr: Option<SocketAddr>,
|
||||
/// CDN replication role: determines budget limits and pull ordering
|
||||
device_role: DeviceRole,
|
||||
/// True if an anchor reported this identity is already connected from elsewhere
|
||||
pub duplicate_detected: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
fn is_public_ip(ip: IpAddr) -> bool {
|
||||
|
|
@ -100,9 +102,10 @@ impl Network {
|
|||
let mut builder = iroh::Endpoint::builder()
|
||||
.secret_key(secret_key)
|
||||
.relay_mode(iroh::RelayMode::Disabled)
|
||||
.alpns(vec![ALPN_V2.to_vec()]);
|
||||
.alpns(vec![ALPN_V2.to_vec()])
|
||||
.clear_address_lookup(); // Remove default pkarr + DNS (no dns.iroh.link publishing)
|
||||
|
||||
// mDNS LAN discovery: enables automatic peer discovery on local network
|
||||
// mDNS LAN discovery only: enables automatic peer discovery on local network
|
||||
builder = builder.address_lookup(
|
||||
iroh::address_lookup::MdnsAddressLookupBuilder::default(),
|
||||
);
|
||||
|
|
@ -271,6 +274,7 @@ impl Network {
|
|||
has_public_v6,
|
||||
bind_addr,
|
||||
device_role,
|
||||
duplicate_detected: Arc::new(AtomicBool::new(false)),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -672,7 +676,7 @@ impl Network {
|
|||
let our_nat_type = conn_handle.nat_type().await;
|
||||
let our_http_capable = conn_handle.is_http_capable();
|
||||
let our_http_addr = conn_handle.http_addr();
|
||||
match initial_exchange_accept(storage, &our_node_id, send, recv, remote_node_id, anchor_addr, Some(remote_sock), our_nat_type, our_http_capable, our_http_addr, conn_handle.device_role(), None).await {
|
||||
match initial_exchange_accept(storage, &our_node_id, send, recv, remote_node_id, anchor_addr, Some(remote_sock), our_nat_type, our_http_capable, our_http_addr, conn_handle.device_role(), None, false).await {
|
||||
Ok(()) => {
|
||||
info!(peer = hex::encode(remote_node_id), "Initial exchange complete (upgraded to mesh)");
|
||||
conn_handle.log_activity(ActivityLevel::Info, ActivityCategory::Connection, format!("Upgraded {} to mesh", &hex::encode(remote_node_id)[..8]), Some(remote_node_id));
|
||||
|
|
@ -723,7 +727,12 @@ impl Network {
|
|||
|
||||
// Initial exchange WITHOUT holding conn_mgr lock
|
||||
match initial_exchange_connect(&self.storage, &self.our_node_id, &conn, peer_id, anchor_addr, our_nat_type, self.is_http_capable(), self.http_addr(), Some(self.device_role), None).await? {
|
||||
ExchangeResult::Accepted => {
|
||||
ExchangeResult::Accepted { duplicate_active } => {
|
||||
if duplicate_active {
|
||||
self.duplicate_detected.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
warn!(peer = hex::encode(peer_id), "Duplicate identity detected by anchor — this identity is active elsewhere");
|
||||
self.log_activity(ActivityLevel::Warn, ActivityCategory::Connection, "Duplicate identity active on network".into(), None);
|
||||
}
|
||||
// Spawn the per-connection stream loop
|
||||
let conn_data = self.conn_handle.get_connection_map().await;
|
||||
if let Some((_, conn, _, last_activity)) = conn_data.into_iter().find(|(nid, _, _, _)| *nid == peer_id) {
|
||||
|
|
@ -1365,7 +1374,7 @@ impl Network {
|
|||
let our_nat_type = self.conn_handle.nat_type().await;
|
||||
|
||||
match initial_exchange_connect(&self.storage, &self.our_node_id, &conn, peer_id, anchor_addr, our_nat_type, self.is_http_capable(), self.http_addr(), Some(self.device_role), None).await {
|
||||
Ok(ExchangeResult::Accepted) => {
|
||||
Ok(ExchangeResult::Accepted { .. }) => {
|
||||
self.conn_handle.register_connection(peer_id, conn.clone(), vec![], PeerSlotKind::Local).await;
|
||||
{
|
||||
let s = self.storage.get().await;
|
||||
|
|
@ -1470,7 +1479,7 @@ impl Network {
|
|||
let conn = self.conn_handle.get_connection(peer_id).await;
|
||||
if let Some(conn) = conn {
|
||||
match initial_exchange_connect(&self.storage, &self.our_node_id, &conn, *peer_id, anchor_addr.clone(), our_nat_type, self.is_http_capable(), self.http_addr(), Some(self.device_role), None).await {
|
||||
Ok(ExchangeResult::Accepted) => {}
|
||||
Ok(ExchangeResult::Accepted { .. }) => {}
|
||||
Ok(ExchangeResult::Refused { redirect }) => {
|
||||
debug!(peer = hex::encode(peer_id), "Auto-connect refused, disconnecting");
|
||||
self.conn_handle.disconnect_peer(peer_id).await;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue