diff --git a/core/kordophone-db/src/repository.rs b/core/kordophone-db/src/repository.rs index 79b6e40..17728c1 100644 --- a/core/kordophone-db/src/repository.rs +++ b/core/kordophone-db/src/repository.rs @@ -264,16 +264,34 @@ impl<'a> Repository<'a> { .order_by(schema::messages::date.asc()) .load::(self.connection)?; + let sender_handles: Vec = message_records + .iter() + .filter_map(|record| record.sender_participant_handle.clone()) + .collect(); + + let participant_map: HashMap = if sender_handles.is_empty() { + HashMap::new() + } else { + participants + .filter(handle.eq_any(&sender_handles)) + .load::(self.connection)? + .into_iter() + .map(|participant| { + let key = participant.handle.clone(); + (key, participant.into()) + }) + .collect() + }; + let mut result = Vec::new(); for message_record in message_records { let mut message: Message = message_record.clone().into(); // If the message references a sender participant, load the participant info if let Some(sender_handle) = message_record.sender_participant_handle { - let participant = participants - .find(sender_handle) - .first::(self.connection)?; - message.sender = participant.into(); + if let Some(participant) = participant_map.get(&sender_handle) { + message.sender = participant.clone(); + } } result.push(message); @@ -307,8 +325,8 @@ impl<'a> Repository<'a> { } pub fn delete_all_messages(&mut self) -> Result<()> { - use crate::schema::messages::dsl as messages_dsl; use crate::schema::message_aliases::dsl as aliases_dsl; + use crate::schema::messages::dsl as messages_dsl; diesel::delete(messages_dsl::messages).execute(self.connection)?; diesel::delete(aliases_dsl::message_aliases).execute(self.connection)?; diff --git a/core/kordophoned/include/net.buzzert.kordophonecd.Server.xml b/core/kordophoned/include/net.buzzert.kordophonecd.Server.xml index cdef983..f0dcf0b 100644 --- a/core/kordophoned/include/net.buzzert.kordophonecd.Server.xml +++ b/core/kordophoned/include/net.buzzert.kordophonecd.Server.xml @@ -73,14 +73,13 @@ 'sender' (string): Sender display name 'attachments' (array of dictionaries): List of attachments 'guid' (string): Attachment GUID - 'path' (string): Attachment path - 'preview_path' (string): Preview attachment path 'downloaded' (boolean): Whether the attachment is downloaded 'preview_downloaded' (boolean): Whether the preview is downloaded 'metadata' (dictionary, optional): Attachment metadata 'attribution_info' (dictionary, optional): Attribution info 'width' (int32, optional): Width - 'height' (int32, optional): Height"/> + 'height' (int32, optional): Height + Use GetAttachmentInfo for full/preview paths."/> diff --git a/core/kordophoned/src/daemon/contact_resolver/eds.rs b/core/kordophoned/src/daemon/contact_resolver/eds.rs index 98f5bd5..12e6697 100644 --- a/core/kordophoned/src/daemon/contact_resolver/eds.rs +++ b/core/kordophoned/src/daemon/contact_resolver/eds.rs @@ -6,6 +6,8 @@ use std::collections::HashMap; use std::sync::Mutex; use std::time::Duration; +const LOOKUP_TIMEOUT: Duration = Duration::from_secs(2); + #[derive(Clone)] pub struct EDSContactResolverBackend; @@ -189,11 +191,10 @@ impl ContactResolverBackend for EDSContactResolverBackend { None => return None, }; - let address_book_proxy = handle.connection.with_proxy( - &handle.bus_name, - &handle.object_path, - Duration::from_secs(60), - ); + let address_book_proxy = + handle + .connection + .with_proxy(&handle.bus_name, &handle.object_path, LOOKUP_TIMEOUT); ensure_address_book_open(&address_book_proxy); @@ -255,11 +256,10 @@ impl ContactResolverBackend for EDSContactResolverBackend { None => return None, }; - let address_book_proxy = handle.connection.with_proxy( - &handle.bus_name, - &handle.object_path, - Duration::from_secs(60), - ); + let address_book_proxy = + handle + .connection + .with_proxy(&handle.bus_name, &handle.object_path, LOOKUP_TIMEOUT); ensure_address_book_open(&address_book_proxy); diff --git a/core/kordophoned/src/daemon/mod.rs b/core/kordophoned/src/daemon/mod.rs index bb17208..c63bff3 100644 --- a/core/kordophoned/src/daemon/mod.rs +++ b/core/kordophoned/src/daemon/mod.rs @@ -185,14 +185,14 @@ impl Daemon { async fn handle_event(&mut self, event: Event) { match event { Event::GetVersion(reply) => { - reply.send(self.version.clone()).unwrap(); + let _ = reply.send(self.version.clone()); } Event::SyncConversationList(reply) => { self.spawn_conversation_list_sync(); // This is a background operation, so return right away. - reply.send(()).unwrap(); + let _ = reply.send(()); } Event::SyncAllConversations(reply) => { @@ -207,7 +207,7 @@ impl Daemon { }); // This is a background operation, so return right away. - reply.send(()).unwrap(); + let _ = reply.send(()); } Event::SyncConversation(conversation_id, reply) => { @@ -225,7 +225,7 @@ impl Daemon { } }); - reply.send(()).unwrap(); + let _ = reply.send(()); } Event::MarkConversationAsRead(conversation_id, reply) => { @@ -237,7 +237,7 @@ impl Daemon { } }); - reply.send(()).unwrap(); + let _ = reply.send(()); } Event::UpdateConversationMetadata(conversation, reply) => { @@ -250,7 +250,7 @@ impl Daemon { } }); - reply.send(()).unwrap(); + let _ = reply.send(()); } Event::UpdateStreamReconnected => { @@ -268,7 +268,7 @@ impl Daemon { Event::GetAllConversations(limit, offset, reply) => { let conversations = self.get_conversations_limit_offset(limit, offset).await; - reply.send(conversations).unwrap(); + let _ = reply.send(conversations); } Event::GetAllSettings(reply) => { @@ -277,7 +277,7 @@ impl Daemon { Settings::default() }); - reply.send(settings).unwrap(); + let _ = reply.send(settings); } Event::UpdateSettings(settings, reply) => { @@ -309,12 +309,12 @@ impl Daemon { } } - reply.send(()).unwrap(); + let _ = reply.send(()); } Event::GetMessages(conversation_id, last_message_id, reply) => { let messages = self.get_messages(conversation_id, last_message_id).await; - reply.send(messages).unwrap(); + let _ = reply.send(messages); } Event::DeleteAllConversations(reply) => { @@ -322,7 +322,7 @@ impl Daemon { log::error!(target: target::SYNC, "Failed to delete all conversations: {}", e); }); - reply.send(()).unwrap(); + let _ = reply.send(()); } Event::SendMessage(conversation_id, text, attachment_guids, reply) => { @@ -330,7 +330,7 @@ impl Daemon { let uuid = self .enqueue_outgoing_message(text, conversation_id.clone(), attachment_guids) .await; - reply.send(uuid).unwrap(); + let _ = reply.send(uuid); // Send message updated signal, we have a placeholder message we will return. self.signal_sender @@ -395,7 +395,7 @@ impl Daemon { .await .unwrap(); - reply.send(()).unwrap(); + let _ = reply.send(()); } Event::AttachmentDownloaded(attachment_id) => { diff --git a/core/kordophoned/src/dbus/agent.rs b/core/kordophoned/src/dbus/agent.rs index d284132..7ffbae2 100644 --- a/core/kordophoned/src/dbus/agent.rs +++ b/core/kordophoned/src/dbus/agent.rs @@ -176,9 +176,8 @@ impl DBusAgent { &self, make_event: impl FnOnce(Reply) -> Event + Send, ) -> Result { - run_sync_future(self.send_event(make_event)) - .unwrap() - .map_err(|e| MethodErr::failed(&format!("Daemon error: {}", e))) + let daemon_result = run_sync_future(self.send_event(make_event))?; + daemon_result.map_err(|e| MethodErr::failed(&format!("Daemon error: {}", e))) } fn resolve_participant_display_name(&mut self, participant: &Participant) -> String { @@ -284,104 +283,86 @@ impl DbusRepository for DBusAgent { Some(last_message_id) }; - self.send_event_sync(|r| Event::GetMessages(conversation_id, last_message_id_opt, r)) - .map(|messages| { - messages + let messages = + self.send_event_sync(|r| Event::GetMessages(conversation_id, last_message_id_opt, r))?; + + let mapped: Vec = messages + .into_iter() + .map(|msg| { + let mut map = arg::PropMap::new(); + map.insert("id".into(), arg::Variant(Box::new(msg.id))); + + // Remove the attachment placeholder here. + let text = msg.text.replace("\u{FFFC}", ""); + + map.insert("text".into(), arg::Variant(Box::new(text))); + map.insert( + "date".into(), + arg::Variant(Box::new(msg.date.and_utc().timestamp())), + ); + map.insert( + "sender".into(), + arg::Variant(Box::new(msg.sender.display_name())), + ); + + let attachments: Vec = msg + .attachments .into_iter() - .map(|msg| { - let mut map = arg::PropMap::new(); - map.insert("id".into(), arg::Variant(Box::new(msg.id))); - - // Remove the attachment placeholder here. - let text = msg.text.replace("\u{FFFC}", ""); - - map.insert("text".into(), arg::Variant(Box::new(text))); - map.insert( - "date".into(), - arg::Variant(Box::new(msg.date.and_utc().timestamp())), + .map(|attachment| { + let mut attachment_map = arg::PropMap::new(); + attachment_map.insert( + "guid".into(), + arg::Variant(Box::new(attachment.guid.clone())), ); - map.insert( - "sender".into(), - arg::Variant(Box::new( - self.resolve_participant_display_name(&msg.sender.into()), - )), + attachment_map.insert( + "downloaded".into(), + arg::Variant(Box::new(attachment.is_downloaded(false))), + ); + attachment_map.insert( + "preview_downloaded".into(), + arg::Variant(Box::new(attachment.is_downloaded(true))), ); - // Attachments array - let attachments: Vec = msg - .attachments - .into_iter() - .map(|attachment| { - let mut attachment_map = arg::PropMap::new(); - attachment_map.insert( - "guid".into(), - arg::Variant(Box::new(attachment.guid.clone())), - ); + if let Some(ref metadata) = attachment.metadata { + let mut metadata_map = arg::PropMap::new(); - // Paths and download status - let path = attachment.get_path_for_preview(false); - let preview_path = attachment.get_path_for_preview(true); - let downloaded = attachment.is_downloaded(false); - let preview_downloaded = attachment.is_downloaded(true); - - attachment_map.insert( - "path".into(), - arg::Variant(Box::new(path.to_string_lossy().to_string())), - ); - attachment_map.insert( - "preview_path".into(), - arg::Variant(Box::new( - preview_path.to_string_lossy().to_string(), - )), - ); - attachment_map.insert( - "downloaded".into(), - arg::Variant(Box::new(downloaded)), - ); - attachment_map.insert( - "preview_downloaded".into(), - arg::Variant(Box::new(preview_downloaded)), - ); - - // Metadata - if let Some(ref metadata) = attachment.metadata { - let mut metadata_map = arg::PropMap::new(); - - if let Some(ref attribution_info) = metadata.attribution_info { - let mut attribution_map = arg::PropMap::new(); - if let Some(width) = attribution_info.width { - attribution_map.insert( - "width".into(), - arg::Variant(Box::new(width as i32)), - ); - } - if let Some(height) = attribution_info.height { - attribution_map.insert( - "height".into(), - arg::Variant(Box::new(height as i32)), - ); - } - metadata_map.insert( - "attribution_info".into(), - arg::Variant(Box::new(attribution_map)), - ); - } - - attachment_map.insert( - "metadata".into(), - arg::Variant(Box::new(metadata_map)), + if let Some(ref attribution_info) = metadata.attribution_info { + let mut attribution_map = arg::PropMap::new(); + if let Some(width) = attribution_info.width { + attribution_map.insert( + "width".into(), + arg::Variant(Box::new(width as i32)), ); } + if let Some(height) = attribution_info.height { + attribution_map.insert( + "height".into(), + arg::Variant(Box::new(height as i32)), + ); + } + metadata_map.insert( + "attribution_info".into(), + arg::Variant(Box::new(attribution_map)), + ); + } - attachment_map - }) - .collect(); + attachment_map.insert( + "metadata".into(), + arg::Variant(Box::new(metadata_map)), + ); + } - map.insert("attachments".into(), arg::Variant(Box::new(attachments))); - map + attachment_map }) - .collect() + .collect(); + + map.insert("attachments".into(), arg::Variant(Box::new(attachments))); + + map }) + .collect(); + + Ok(mapped) } fn delete_all_conversations(&mut self) -> Result<(), MethodErr> { @@ -496,7 +477,7 @@ where T: Send, F: Future + Send, { - thread::scope(move |s| { + let joined = thread::scope(move |s| { s.spawn(move || { let rt = tokio::runtime::Builder::new_current_thread() .enable_all() @@ -507,6 +488,10 @@ where Ok(result) }) .join() - }) - .expect("Error joining runtime thread") + }); + + match joined { + Ok(result) => result, + Err(_) => Err(MethodErr::failed("Error joining runtime thread")), + } } diff --git a/core/kpcli/src/daemon/dbus.rs b/core/kpcli/src/daemon/dbus.rs index 96d1178..dc4e444 100644 --- a/core/kpcli/src/daemon/dbus.rs +++ b/core/kpcli/src/daemon/dbus.rs @@ -33,7 +33,7 @@ impl DBusDaemonInterface { fn proxy(&self) -> Proxy<&Connection> { self.conn - .with_proxy(DBUS_NAME, DBUS_PATH, std::time::Duration::from_millis(5000)) + .with_proxy(DBUS_NAME, DBUS_PATH, std::time::Duration::from_secs(30)) } async fn print_settings(&mut self) -> Result<()> { diff --git a/gtk/src/models/attachment.vala b/gtk/src/models/attachment.vala index 24e9596..f97353d 100644 --- a/gtk/src/models/attachment.vala +++ b/gtk/src/models/attachment.vala @@ -44,11 +44,11 @@ public class AttachmentInfo : Object { } public class Attachment : Object { - public string guid; - public string path; - public string preview_path; - public bool downloaded; - public bool preview_downloaded; + public string guid = ""; + public string path = ""; + public string preview_path = ""; + public bool downloaded = false; + public bool preview_downloaded = false; public AttachmentMetadata? metadata; public Attachment(string guid, AttachmentMetadata? metadata) { diff --git a/gtk/src/service/interface/xml/net.buzzert.kordophonecd.Server.xml b/gtk/src/service/interface/xml/net.buzzert.kordophonecd.Server.xml index cdef983..f0dcf0b 100644 --- a/gtk/src/service/interface/xml/net.buzzert.kordophonecd.Server.xml +++ b/gtk/src/service/interface/xml/net.buzzert.kordophonecd.Server.xml @@ -73,14 +73,13 @@ 'sender' (string): Sender display name 'attachments' (array of dictionaries): List of attachments 'guid' (string): Attachment GUID - 'path' (string): Attachment path - 'preview_path' (string): Preview attachment path 'downloaded' (boolean): Whether the attachment is downloaded 'preview_downloaded' (boolean): Whether the preview is downloaded 'metadata' (dictionary, optional): Attachment metadata 'attribution_info' (dictionary, optional): Attribution info 'width' (int32, optional): Width - 'height' (int32, optional): Height"/> + 'height' (int32, optional): Height + Use GetAttachmentInfo for full/preview paths."/> diff --git a/gtk/src/transcript/transcript-drawing-area.vala b/gtk/src/transcript/transcript-drawing-area.vala index 2417f23..df05ba6 100644 --- a/gtk/src/transcript/transcript-drawing-area.vala +++ b/gtk/src/transcript/transcript-drawing-area.vala @@ -363,6 +363,21 @@ private class TranscriptDrawingArea : Widget // Check for attachments. For each one, add an image layout bubble foreach (var attachment in message.attachments) { + string preview_path = attachment.preview_path; + bool preview_downloaded = attachment.preview_downloaded; + + if (preview_path == null || preview_path == "") { + try { + var attachment_info = Repository.get_instance().get_attachment_info(attachment.guid); + if (attachment_info.preview_path != null) { + preview_path = attachment_info.preview_path; + } + preview_downloaded = attachment_info.preview_downloaded == true; + } catch (GLib.Error e) { + warning("Failed to load attachment info for %s: %s", attachment.guid, e.message); + } + } + Graphene.Size? image_size = null; if (attachment.metadata != null) { image_size = Graphene.Size() { @@ -371,7 +386,7 @@ private class TranscriptDrawingArea : Widget }; } - var image_layout = new ImageBubbleLayout(attachment.preview_path, message.from_me, this, max_width, image_size); + var image_layout = new ImageBubbleLayout(preview_path, message.from_me, this, max_width, image_size); image_layout.id = @"image-$(attachment.guid)"; image_layout.attachment_guid = attachment.guid; @@ -379,12 +394,12 @@ private class TranscriptDrawingArea : Widget start_animation(image_layout.id); } - image_layout.is_downloaded = attachment.preview_downloaded; + image_layout.is_downloaded = preview_downloaded; items.add(image_layout); // If the attachment isn't downloaded, queue a download since we are going to be showing it here. // TODO: Probably would be better if we only did this for stuff in the viewport. - if (!attachment.preview_downloaded) { + if (!preview_downloaded) { try { Repository.get_instance().download_attachment(attachment.guid, true); } catch (GLib.Error e) { @@ -447,4 +462,4 @@ public struct VisibleLayout { this.bubble = bubble; this.rect = rect; } -} \ No newline at end of file +}