Skip to content

Commit 1413e39

Browse files
authored
Merge pull request #19 from VectorPrivacy/mls-chatrooms
fix: a multitude of Group Chat event bugs
2 parents f9e1d5c + bb5791a commit 1413e39

3 files changed

Lines changed: 187 additions & 26 deletions

File tree

src-tauri/src/lib.rs

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2445,7 +2445,7 @@ async fn notifs() -> Result<bool, String> {
24452445
match process_rumor(rumor_event, rumor_context).await {
24462446
Ok(result) => {
24472447
match result {
2448-
RumorProcessingResult::TextMessage(message) => {
2448+
RumorProcessingResult::TextMessage(message) | RumorProcessingResult::FileAttachment(message) => {
24492449
// Add to unified storage
24502450
let was_added = {
24512451
let mut state = crate::STATE.lock().await;
@@ -2458,11 +2458,50 @@ async fn notifs() -> Result<bool, String> {
24582458
None
24592459
}
24602460
}
2461-
_ => None,
2461+
RumorProcessingResult::Reaction(reaction) => {
2462+
// Handle reactions in real-time
2463+
let was_added = {
2464+
let mut state = crate::STATE.lock().await;
2465+
if let Some((chat_id, msg)) = state.find_chat_and_message_mut(&reaction.reference_id) {
2466+
msg.add_reaction(reaction.clone(), Some(chat_id))
2467+
} else {
2468+
false
2469+
}
2470+
};
2471+
2472+
if was_added {
2473+
println!("[MLS][live] Reaction added: {}", reaction.reference_id);
2474+
}
2475+
None // Don't emit as message
2476+
}
2477+
RumorProcessingResult::TypingIndicator { profile_id, until } => {
2478+
// Handle typing indicators in real-time
2479+
let active_typers = {
2480+
let mut state = crate::STATE.lock().await;
2481+
if let Some(chat) = state.get_chat_mut(&group_id_for_persist) {
2482+
chat.update_typing_participant(profile_id.clone(), until);
2483+
chat.get_active_typers()
2484+
} else {
2485+
Vec::new()
2486+
}
2487+
};
2488+
2489+
// Emit typing update event
2490+
if let Some(handle) = TAURI_APP.get() {
2491+
let _ = handle.emit("typing-update", serde_json::json!({
2492+
"chat_id": group_id_for_persist,
2493+
"active_typers": active_typers
2494+
}));
2495+
}
2496+
2497+
println!("[MLS][live] Typing indicator processed for group: {}", group_id_for_persist);
2498+
None // Don't emit as message
2499+
}
2500+
RumorProcessingResult::Ignored => None,
24622501
}
24632502
}
24642503
Err(e) => {
2465-
eprintln!("[MLS] Failed to process rumor: {}", e);
2504+
eprintln!("[MLS][live] Failed to process rumor: {}", e);
24662505
None
24672506
}
24682507
}
@@ -2798,8 +2837,13 @@ async fn generate_blurhash_preview(npub: String, msg_id: String) -> Result<Strin
27982837
let mut found_attachment = None;
27992838

28002839
for chat in &state.chats {
2801-
// Check if this chat involves the specified profile
2802-
if chat.has_participant(&npub) {
2840+
// Check if this is the target chat (works for both DMs and group chats)
2841+
let is_target_chat = match &chat.chat_type {
2842+
ChatType::MlsGroup => chat.id == npub,
2843+
ChatType::DirectMessage => chat.has_participant(&npub),
2844+
};
2845+
2846+
if is_target_chat {
28032847
// Look for the message in this chat
28042848
if let Some(message) = chat.messages.iter().find(|m| m.id == msg_id) {
28052849
// Get the first attachment
@@ -2834,7 +2878,13 @@ async fn download_attachment(npub: String, msg_id: String, attachment_id: String
28342878
// Find the message and attachment in chats
28352879
let mut found_attachment = None;
28362880
for chat in &mut state.chats {
2837-
if chat.has_participant(&npub) {
2881+
// For group chats, npub is the group_id; for DMs, it's a participant npub
2882+
let is_target_chat = match &chat.chat_type {
2883+
ChatType::MlsGroup => chat.id == npub,
2884+
ChatType::DirectMessage => chat.has_participant(&npub),
2885+
};
2886+
2887+
if is_target_chat {
28382888
if let Some(message) = chat.messages.iter_mut().find(|m| m.id == msg_id) {
28392889
if let Some(attachment) = message.attachments.iter_mut().find(|a| a.id == attachment_id) {
28402890
// Check that we're not already downloading
@@ -2875,7 +2925,12 @@ async fn download_attachment(npub: String, msg_id: String, attachment_id: String
28752925

28762926
// Find and update the attachment status
28772927
for chat in &mut state.chats {
2878-
if chat.has_participant(&npub) {
2928+
let is_target_chat = match &chat.chat_type {
2929+
ChatType::MlsGroup => chat.id == npub,
2930+
ChatType::DirectMessage => chat.has_participant(&npub),
2931+
};
2932+
2933+
if is_target_chat {
28792934
if let Some(message) = chat.messages.iter_mut().find(|m| m.id == msg_id) {
28802935
if let Some(attachment) = message.attachments.iter_mut().find(|a| a.id == attachment_id) {
28812936
attachment.downloading = false;
@@ -2909,7 +2964,12 @@ async fn download_attachment(npub: String, msg_id: String, attachment_id: String
29092964

29102965
// Find and update the attachment status
29112966
for chat in &mut state.chats {
2912-
if chat.has_participant(&npub) {
2967+
let is_target_chat = match &chat.chat_type {
2968+
ChatType::MlsGroup => chat.id == npub,
2969+
ChatType::DirectMessage => chat.has_participant(&npub),
2970+
};
2971+
2972+
if is_target_chat {
29132973
if let Some(message) = chat.messages.iter_mut().find(|m| m.id == msg_id) {
29142974
if let Some(attachment) = message.attachments.iter_mut().find(|a| a.id == attachment_id) {
29152975
attachment.downloading = false;
@@ -2944,7 +3004,12 @@ async fn download_attachment(npub: String, msg_id: String, attachment_id: String
29443004

29453005
// Find and update the attachment
29463006
for chat in &mut state.chats {
2947-
if chat.has_participant(&npub) {
3007+
let is_target_chat = match &chat.chat_type {
3008+
ChatType::MlsGroup => chat.id == npub,
3009+
ChatType::DirectMessage => chat.has_participant(&npub),
3010+
};
3011+
3012+
if is_target_chat {
29483013
if let Some(message) = chat.messages.iter_mut().find(|m| m.id == msg_id) {
29493014
if let Some(attachment_index) = message.attachments.iter().position(|a| a.id == attachment_id) {
29503015
let attachment = &mut message.attachments[attachment_index];
@@ -4937,6 +5002,7 @@ pub fn run() {
49375002
message::voice_message,
49385003
message::file_message,
49395004
message::react,
5005+
message::react_to_message,
49405006
message::fetch_msg_metadata,
49415007
fetch_messages,
49425008
warmup_nip96_servers,

src-tauri/src/message.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,70 @@ pub async fn react(reference_id: String, npub: String, emoji: String) -> Result<
941941
}
942942
}
943943

944+
/// Protocol-agnostic reaction function that works for both DMs and Group Chats
945+
#[tauri::command]
946+
pub async fn react_to_message(reference_id: String, chat_id: String, emoji: String) -> Result<bool, String> {
947+
use crate::chat::ChatType;
948+
949+
let client = NOSTR_CLIENT.get().expect("Nostr client not initialized");
950+
let signer = client.signer().await.map_err(|e| e.to_string())?;
951+
let my_public_key = signer.get_public_key().await.map_err(|e| e.to_string())?;
952+
953+
// Determine chat type
954+
let state = STATE.lock().await;
955+
let chat = state.chats.iter().find(|c| c.id == chat_id)
956+
.ok_or_else(|| "Chat not found".to_string())?;
957+
let chat_type = chat.chat_type.clone();
958+
drop(state);
959+
960+
match chat_type {
961+
ChatType::DirectMessage => {
962+
// For DMs, use the existing DM reaction logic
963+
react(reference_id, chat_id, emoji).await.map_err(|_| "DM reaction failed".to_string())
964+
}
965+
ChatType::MlsGroup => {
966+
// For group chats, send reaction through MLS
967+
let reference_event = EventId::from_hex(&reference_id).map_err(|e| e.to_string())?;
968+
969+
// Build reaction rumor manually (simpler than using the builder for group chats)
970+
let rumor = EventBuilder::new(Kind::Reaction, &emoji)
971+
.tag(Tag::event(reference_event))
972+
.build(my_public_key);
973+
let rumor_id = rumor.id.ok_or("Failed to get rumor ID")?.to_hex();
974+
975+
// Send through MLS
976+
crate::mls::send_mls_message(&chat_id, rumor).await?;
977+
978+
// Add reaction to local state
979+
let reaction = Reaction {
980+
id: rumor_id,
981+
reference_id: reference_id.clone(),
982+
author_id: my_public_key.to_hex(),
983+
emoji,
984+
};
985+
986+
let mut state = STATE.lock().await;
987+
if let Some(chat) = state.chats.iter_mut().find(|c| c.id == chat_id) {
988+
if let Some(msg) = chat.messages.iter_mut().find(|m| m.id == reference_id) {
989+
let was_added = msg.add_reaction(reaction, Some(&chat_id));
990+
991+
if was_added {
992+
// Save to database
993+
if let Some(handle) = TAURI_APP.get() {
994+
let all_messages = chat.messages.clone();
995+
let _ = save_chat_messages(handle.clone(), &chat.id, &all_messages).await;
996+
}
997+
}
998+
999+
return Ok(was_added);
1000+
}
1001+
}
1002+
1003+
Ok(false)
1004+
}
1005+
}
1006+
}
1007+
9441008
#[tauri::command]
9451009
pub async fn fetch_msg_metadata(chat_id: String, msg_id: String) -> bool {
9461010
// Find the message we're extracting metadata from

0 commit comments

Comments
 (0)