v0.0.17: fix /r reply in TUI, /p shortcut, /eth, /unalias
TUI fixes: - /r and /reply now work: tracks last_dm_peer from received messages - /r switches peer to last DM sender, then type normally - /p @alias works as shortcut for /peer @alias - /eth shows Ethereum address in TUI - /unalias removes your alias Web fixes: - /p @alias and /peer @alias resolve and set peer - /r and /reply work (switch to last DM sender) - /unalias removes alias - /admin-unalias <alias> <password> for admin removal - File download now shows as clickable link (not auto-download) Server: - POST /v1/alias/unregister — remove own alias - POST /v1/alias/admin-remove — admin removes any alias - WARZONE_ADMIN_PASSWORD env var (default: "admin") Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -125,6 +125,12 @@ pub fn save_seed(seed: &Seed) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Load raw seed bytes (for deriving eth address etc).
|
||||
pub fn load_seed_raw() -> anyhow::Result<[u8; 32]> {
|
||||
let seed = load_seed()?;
|
||||
Ok(seed.0)
|
||||
}
|
||||
|
||||
/// Load seed, decrypting if necessary.
|
||||
pub fn load_seed() -> anyhow::Result<Seed> {
|
||||
let path = seed_path();
|
||||
|
||||
@@ -52,7 +52,7 @@ pub struct App {
|
||||
pub peer_fp: Option<String>,
|
||||
pub server_url: String,
|
||||
pub should_quit: bool,
|
||||
pub last_dm_peer: Option<String>,
|
||||
pub last_dm_peer: Arc<Mutex<Option<String>>>,
|
||||
/// Track receipt status for messages we sent, keyed by message ID.
|
||||
pub receipts: Arc<Mutex<HashMap<String, ReceiptStatus>>>,
|
||||
/// Pending incoming file transfers, keyed by file ID.
|
||||
@@ -112,7 +112,7 @@ impl App {
|
||||
peer_fp,
|
||||
server_url,
|
||||
should_quit: false,
|
||||
last_dm_peer: None,
|
||||
last_dm_peer: Arc::new(Mutex::new(None)),
|
||||
receipts: Arc::new(Mutex::new(HashMap::new())),
|
||||
pending_files: Arc::new(Mutex::new(HashMap::new())),
|
||||
}
|
||||
@@ -285,9 +285,17 @@ impl App {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if text == "/eth" {
|
||||
// Show ethereum address from seed
|
||||
if let Ok(seed) = crate::keystore::load_seed_raw() {
|
||||
let eth = warzone_protocol::ethereum::derive_eth_identity(&seed);
|
||||
self.add_message(ChatLine { sender: "system".into(), text: format!("ETH: {}", eth.address.to_checksum()), is_system: true, is_self: false, message_id: None });
|
||||
}
|
||||
return;
|
||||
}
|
||||
if text == "/r" || text == "/reply" {
|
||||
// Just switch to last DM peer
|
||||
if let Some(ref peer) = self.last_dm_peer.clone() {
|
||||
let last = self.last_dm_peer.lock().unwrap().clone();
|
||||
if let Some(ref peer) = last {
|
||||
self.peer_fp = Some(peer.clone());
|
||||
self.add_message(ChatLine { sender: "system".into(), text: format!("→ switched to {}", &peer[..peer.len().min(16)]), is_system: true, is_self: false, message_id: None });
|
||||
} else {
|
||||
@@ -1109,9 +1117,10 @@ fn process_incoming(
|
||||
pending_files: &Arc<Mutex<HashMap<String, PendingFileTransfer>>>,
|
||||
our_fp: &str,
|
||||
client: &ServerClient,
|
||||
last_dm_peer: &Arc<Mutex<Option<String>>>,
|
||||
) {
|
||||
match bincode::deserialize::<WireMessage>(raw) {
|
||||
Ok(wire) => process_wire_message(wire, identity, db, messages, receipts, pending_files, our_fp, client),
|
||||
Ok(wire) => process_wire_message(wire, identity, db, messages, receipts, pending_files, our_fp, client, last_dm_peer),
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
@@ -1125,6 +1134,7 @@ fn process_wire_message(
|
||||
pending_files: &Arc<Mutex<HashMap<String, PendingFileTransfer>>>,
|
||||
our_fp: &str,
|
||||
client: &ServerClient,
|
||||
last_dm_peer: &Arc<Mutex<Option<String>>>,
|
||||
) {
|
||||
match wire {
|
||||
WireMessage::KeyExchange {
|
||||
@@ -1161,6 +1171,7 @@ fn process_wire_message(
|
||||
Ok(plaintext) => {
|
||||
let text = String::from_utf8_lossy(&plaintext).to_string();
|
||||
let _ = db.save_session(&sender_fp, &state);
|
||||
*last_dm_peer.lock().unwrap() = Some(sender_fingerprint.clone());
|
||||
messages.lock().unwrap().push(ChatLine {
|
||||
sender: sender_fingerprint[..sender_fingerprint.len().min(12)].to_string(),
|
||||
text,
|
||||
@@ -1168,7 +1179,6 @@ fn process_wire_message(
|
||||
is_self: false,
|
||||
message_id: None,
|
||||
});
|
||||
// Send delivery receipt
|
||||
send_receipt(our_fp, &sender_fingerprint, &id, ReceiptType::Delivered, client);
|
||||
}
|
||||
Err(_) => {}
|
||||
@@ -1191,6 +1201,7 @@ fn process_wire_message(
|
||||
Ok(plaintext) => {
|
||||
let text = String::from_utf8_lossy(&plaintext).to_string();
|
||||
let _ = db.save_session(&sender_fp, &state);
|
||||
*last_dm_peer.lock().unwrap() = Some(sender_fingerprint.clone());
|
||||
messages.lock().unwrap().push(ChatLine {
|
||||
sender: sender_fingerprint[..sender_fingerprint.len().min(12)].to_string(),
|
||||
text,
|
||||
@@ -1414,6 +1425,7 @@ pub async fn poll_loop(
|
||||
identity: IdentityKeyPair,
|
||||
db: Arc<LocalDb>,
|
||||
client: ServerClient,
|
||||
last_dm_peer: Arc<Mutex<Option<String>>>,
|
||||
) {
|
||||
let fp = normfp(&our_fp);
|
||||
|
||||
@@ -1439,7 +1451,7 @@ pub async fn poll_loop(
|
||||
|
||||
while let Some(Ok(msg)) = read.next().await {
|
||||
if let tokio_tungstenite::tungstenite::Message::Binary(data) = msg {
|
||||
process_incoming(&data, &identity, &db, &messages, &receipts, &pending_files, &our_fp, &client);
|
||||
process_incoming(&data, &identity, &db, &messages, &receipts, &pending_files, &our_fp, &client, &last_dm_peer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1460,7 +1472,7 @@ pub async fn poll_loop(
|
||||
Err(_) => continue,
|
||||
};
|
||||
for raw in &raw_msgs {
|
||||
process_incoming(raw, &identity, &db, &messages, &receipts, &pending_files, &our_fp, &client);
|
||||
process_incoming(raw, &identity, &db, &messages, &receipts, &pending_files, &our_fp, &client, &last_dm_peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1487,12 +1499,13 @@ pub async fn run_tui(
|
||||
let poll_messages = app.messages.clone();
|
||||
let poll_receipts = app.receipts.clone();
|
||||
let poll_pending_files = app.pending_files.clone();
|
||||
let poll_last_dm = app.last_dm_peer.clone();
|
||||
let poll_client = client.clone();
|
||||
let poll_db = db.clone();
|
||||
let poll_fp = our_fp.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
poll_loop(poll_messages, poll_receipts, poll_pending_files, poll_fp, poll_identity, poll_db, poll_client).await;
|
||||
poll_loop(poll_messages, poll_receipts, poll_pending_files, poll_fp, poll_identity, poll_db, poll_client, poll_last_dm).await;
|
||||
});
|
||||
|
||||
loop {
|
||||
|
||||
Reference in New Issue
Block a user