feat: TUI /call, /accept, /reject, /hangup commands (FC-P2-T1+T2+T3)
- /call [fp|@alias|0x...] — send CallSignal::Offer to peer - /accept — answer incoming call (uses last_dm_peer) - /reject — reject incoming call - /hangup — end active call - All four added to /help text Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -69,6 +69,10 @@ impl App {
|
|||||||
" /gkick <fp> Kick member from group",
|
" /gkick <fp> Kick member from group",
|
||||||
" /gmembers List group members",
|
" /gmembers List group members",
|
||||||
" /file <path> Send a file (max 10MB)",
|
" /file <path> Send a file (max 10MB)",
|
||||||
|
" /call [fp|@alias] Call current peer (or specified peer)",
|
||||||
|
" /accept Accept incoming call",
|
||||||
|
" /reject Reject incoming call",
|
||||||
|
" /hangup End current call",
|
||||||
" /quit, /q Exit",
|
" /quit, /q Exit",
|
||||||
"",
|
"",
|
||||||
"Navigation:",
|
"Navigation:",
|
||||||
@@ -488,6 +492,147 @@ impl App {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if text == "/call" || text.starts_with("/call ") {
|
||||||
|
let target = if text.starts_with("/call ") {
|
||||||
|
let arg = text[6..].trim();
|
||||||
|
if arg.starts_with('@') {
|
||||||
|
match self.resolve_alias(&arg[1..], client).await {
|
||||||
|
Some(fp) => Some(fp),
|
||||||
|
None => return,
|
||||||
|
}
|
||||||
|
} else if arg.starts_with("0x") || arg.starts_with("0X") {
|
||||||
|
match self.resolve_address(arg, client).await {
|
||||||
|
Some(fp) => Some(fp),
|
||||||
|
None => return,
|
||||||
|
}
|
||||||
|
} else if !arg.is_empty() {
|
||||||
|
Some(arg.to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let peer = target.or_else(|| self.peer_fp.clone());
|
||||||
|
let peer = match peer {
|
||||||
|
Some(p) if !p.starts_with('#') => p,
|
||||||
|
_ => {
|
||||||
|
self.add_message(ChatLine { sender: "system".into(), text: "No peer to call. Use /call <fp> or set a peer first.".into(), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let peer_fp_clean = normfp(&peer);
|
||||||
|
let msg_id = uuid::Uuid::new_v4().to_string();
|
||||||
|
let our_pub = identity.public_identity();
|
||||||
|
|
||||||
|
let wire = warzone_protocol::message::WireMessage::CallSignal {
|
||||||
|
id: msg_id.clone(),
|
||||||
|
sender_fingerprint: our_pub.fingerprint.to_string(),
|
||||||
|
signal_type: warzone_protocol::message::CallSignalType::Offer,
|
||||||
|
payload: String::new(),
|
||||||
|
target: peer_fp_clean.clone(),
|
||||||
|
};
|
||||||
|
let encoded = match bincode::serialize(&wire) {
|
||||||
|
Ok(e) => e,
|
||||||
|
Err(e) => {
|
||||||
|
self.add_message(ChatLine { sender: "system".into(), text: format!("Call failed: {}", e), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match client.send_message(&peer_fp_clean, Some(&self.our_fp), &encoded).await {
|
||||||
|
Ok(_) => {
|
||||||
|
let display = self.peer_eth.as_deref()
|
||||||
|
.or(Some(&peer))
|
||||||
|
.map(|s| if s.len() > 16 { format!("{}...", &s[..16]) } else { s.to_string() })
|
||||||
|
.unwrap_or_default();
|
||||||
|
self.add_message(ChatLine { sender: "system".into(), text: format!("📞 Calling {}...", display), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.add_message(ChatLine { sender: "system".into(), text: format!("Call failed: {}", e), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if text == "/accept" {
|
||||||
|
let peer = match self.last_dm_peer.lock().unwrap().clone() {
|
||||||
|
Some(p) => p,
|
||||||
|
None => {
|
||||||
|
self.add_message(ChatLine { sender: "system".into(), text: "No incoming call to accept".into(), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let msg_id = uuid::Uuid::new_v4().to_string();
|
||||||
|
let our_pub = identity.public_identity();
|
||||||
|
let wire = warzone_protocol::message::WireMessage::CallSignal {
|
||||||
|
id: msg_id,
|
||||||
|
sender_fingerprint: our_pub.fingerprint.to_string(),
|
||||||
|
signal_type: warzone_protocol::message::CallSignalType::Answer,
|
||||||
|
payload: String::new(),
|
||||||
|
target: normfp(&peer),
|
||||||
|
};
|
||||||
|
if let Ok(encoded) = bincode::serialize(&wire) {
|
||||||
|
let _ = client.send_message(&normfp(&peer), Some(&self.our_fp), &encoded).await;
|
||||||
|
self.add_message(ChatLine { sender: "system".into(), text: "✓ Call accepted".into(), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if text == "/reject" {
|
||||||
|
let peer = match self.last_dm_peer.lock().unwrap().clone() {
|
||||||
|
Some(p) => p,
|
||||||
|
None => {
|
||||||
|
self.add_message(ChatLine { sender: "system".into(), text: "No incoming call to reject".into(), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let msg_id = uuid::Uuid::new_v4().to_string();
|
||||||
|
let our_pub = identity.public_identity();
|
||||||
|
let wire = warzone_protocol::message::WireMessage::CallSignal {
|
||||||
|
id: msg_id,
|
||||||
|
sender_fingerprint: our_pub.fingerprint.to_string(),
|
||||||
|
signal_type: warzone_protocol::message::CallSignalType::Reject,
|
||||||
|
payload: String::new(),
|
||||||
|
target: normfp(&peer),
|
||||||
|
};
|
||||||
|
if let Ok(encoded) = bincode::serialize(&wire) {
|
||||||
|
let _ = client.send_message(&normfp(&peer), Some(&self.our_fp), &encoded).await;
|
||||||
|
self.add_message(ChatLine { sender: "system".into(), text: "✗ Call rejected".into(), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if text == "/hangup" {
|
||||||
|
let peer = self.peer_fp.clone().or_else(|| self.last_dm_peer.lock().unwrap().clone());
|
||||||
|
let peer = match peer {
|
||||||
|
Some(p) if !p.starts_with('#') => p,
|
||||||
|
_ => {
|
||||||
|
self.add_message(ChatLine { sender: "system".into(), text: "No active call".into(), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let msg_id = uuid::Uuid::new_v4().to_string();
|
||||||
|
let our_pub = identity.public_identity();
|
||||||
|
let wire = warzone_protocol::message::WireMessage::CallSignal {
|
||||||
|
id: msg_id,
|
||||||
|
sender_fingerprint: our_pub.fingerprint.to_string(),
|
||||||
|
signal_type: warzone_protocol::message::CallSignalType::Hangup,
|
||||||
|
payload: String::new(),
|
||||||
|
target: normfp(&peer),
|
||||||
|
};
|
||||||
|
if let Ok(encoded) = bincode::serialize(&wire) {
|
||||||
|
let _ = client.send_message(&normfp(&peer), Some(&self.our_fp), &encoded).await;
|
||||||
|
self.add_message(ChatLine { sender: "system".into(), text: "Call ended".into(), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if text.starts_with("/file ") {
|
if text.starts_with("/file ") {
|
||||||
let path_str = text[6..].trim();
|
let path_str = text[6..].trim();
|
||||||
self.handle_file_send(path_str, identity, db, client).await;
|
self.handle_file_send(path_str, identity, db, client).await;
|
||||||
|
|||||||
Reference in New Issue
Block a user