Private
Public Access
1
0

cargo fmt

This commit is contained in:
2025-06-16 19:26:13 -07:00
parent 75fe4d4608
commit 032573d23b
14 changed files with 168 additions and 113 deletions

View File

@@ -168,8 +168,7 @@ impl<'a> Repository<'a> {
let mut participant_cache: HashMap<String, i32> = HashMap::new(); let mut participant_cache: HashMap<String, i32> = HashMap::new();
// Prepare collections for the batch inserts. // Prepare collections for the batch inserts.
let mut db_messages: Vec<MessageRecord> = let mut db_messages: Vec<MessageRecord> = Vec::with_capacity(in_messages.len());
Vec::with_capacity(in_messages.len());
let mut conv_msg_records: Vec<InsertableConversationMessage> = let mut conv_msg_records: Vec<InsertableConversationMessage> =
Vec::with_capacity(in_messages.len()); Vec::with_capacity(in_messages.len());
@@ -178,7 +177,8 @@ impl<'a> Repository<'a> {
let sender_id = match &message.sender { let sender_id = match &message.sender {
Participant::Me => None, Participant::Me => None,
Participant::Remote { display_name, .. } => { Participant::Remote { display_name, .. } => {
if let Some(cached_participant_id) = participant_cache.get(display_name) { if let Some(cached_participant_id) = participant_cache.get(display_name)
{
Some(*cached_participant_id) Some(*cached_participant_id)
} else { } else {
// Try to load from DB first // Try to load from DB first
@@ -239,19 +239,22 @@ impl<'a> Repository<'a> {
// processed instead of re-querying the DB. // processed instead of re-querying the DB.
if let Some(last_msg) = db_messages.last() { if let Some(last_msg) = db_messages.last() {
use crate::schema::conversations::dsl as conv_dsl; use crate::schema::conversations::dsl as conv_dsl;
diesel::update(conv_dsl::conversations.filter(conv_dsl::id.eq(conversation_guid))) diesel::update(
.set(( conv_dsl::conversations.filter(conv_dsl::id.eq(conversation_guid)),
conv_dsl::date.eq(last_msg.date), )
conv_dsl::last_message_preview.eq::<Option<String>>(Some(last_msg.text.clone())), .set((
)) conv_dsl::date.eq(last_msg.date),
.execute(conn)?; conv_dsl::last_message_preview
.eq::<Option<String>>(Some(last_msg.text.clone())),
))
.execute(conn)?;
} }
Ok(()) Ok(())
})?; })?;
// TODO: May need to update conversation metadata here, but this has a perf impact. // TODO: May need to update conversation metadata here, but this has a perf impact.
// Ideally we would consolidate this in the code above, assuming we're only inserting *new* messages, but // Ideally we would consolidate this in the code above, assuming we're only inserting *new* messages, but
// this may not necessarily be the case. // this may not necessarily be the case.
Ok(()) Ok(())

View File

@@ -26,7 +26,12 @@ pub trait EventSocket {
type UpdateStream: Stream<Item = Result<SocketUpdate, Self::Error>>; type UpdateStream: Stream<Item = Result<SocketUpdate, Self::Error>>;
/// Modern event pipeline /// Modern event pipeline
async fn events(self) -> (Self::EventStream, impl Sink<SinkMessage, Error = Self::Error>); async fn events(
self,
) -> (
Self::EventStream,
impl Sink<SinkMessage, Error = Self::Error>,
);
/// Raw update items from the v1 API. /// Raw update items from the v1 API.
async fn raw_updates(self) -> Self::UpdateStream; async fn raw_updates(self) -> Self::UpdateStream;

View File

@@ -115,19 +115,26 @@ impl<B> AuthSetting for hyper::http::Request<B> {
} }
} }
type WebsocketSink = futures_util::stream::SplitSink<WebSocketStream<tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>>, tungstenite::Message>; type WebsocketSink = futures_util::stream::SplitSink<
type WebsocketStream = futures_util::stream::SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>; WebSocketStream<tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>>,
tungstenite::Message,
>;
type WebsocketStream =
futures_util::stream::SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>;
pub struct WebsocketEventSocket { pub struct WebsocketEventSocket {
sink: Option<WebsocketSink>, sink: Option<WebsocketSink>,
stream: WebsocketStream stream: WebsocketStream,
} }
impl WebsocketEventSocket { impl WebsocketEventSocket {
pub fn new(socket: WebSocketStream<MaybeTlsStream<TcpStream>>) -> Self { pub fn new(socket: WebSocketStream<MaybeTlsStream<TcpStream>>) -> Self {
let (sink, stream) = socket.split(); let (sink, stream) = socket.split();
Self { sink: Some(sink), stream } Self {
sink: Some(sink),
stream,
}
} }
} }
@@ -147,12 +154,10 @@ impl WebsocketEventSocket {
} }
} }
tungstenite::Message::Ping(_) => { tungstenite::Message::Ping(_) => {
// We don't expect the server to send us pings. // We don't expect the server to send us pings.
Ok(None) Ok(None)
} }
tungstenite::Message::Pong(_) => { tungstenite::Message::Pong(_) => Ok(Some(SocketUpdate::Pong)),
Ok(Some(SocketUpdate::Pong))
}
tungstenite::Message::Close(_) => { tungstenite::Message::Close(_) => {
// Connection was closed cleanly // Connection was closed cleanly
Err(Error::ClientError("WebSocket connection closed".into())) Err(Error::ClientError("WebSocket connection closed".into()))
@@ -169,33 +174,40 @@ impl EventSocket for WebsocketEventSocket {
type EventStream = BoxStream<'static, Result<SocketEvent, Error>>; type EventStream = BoxStream<'static, Result<SocketEvent, Error>>;
type UpdateStream = BoxStream<'static, Result<SocketUpdate, Error>>; type UpdateStream = BoxStream<'static, Result<SocketUpdate, Error>>;
async fn events(mut self) -> (Self::EventStream, impl Sink<SinkMessage, Error = Self::Error>) { async fn events(
mut self,
) -> (
Self::EventStream,
impl Sink<SinkMessage, Error = Self::Error>,
) {
use futures_util::stream::iter; use futures_util::stream::iter;
let sink = self.sink.take().unwrap().with(|f| { let sink = self.sink.take().unwrap().with(|f| match f {
match f { SinkMessage::Ping => futures_util::future::ready(Ok::<tungstenite::Message, Error>(
SinkMessage::Ping => futures_util::future::ready(Ok::<tungstenite::Message, Error>(tungstenite::Message::Ping(Bytes::new()))) tungstenite::Message::Ping(Bytes::new()),
} )),
}); });
let stream = self.raw_update_stream() let stream = self
.map_ok(|updates| -> BoxStream<'static, Result<SocketEvent, Error>> { .raw_update_stream()
match updates { .map_ok(
SocketUpdate::Update(updates) => { |updates| -> BoxStream<'static, Result<SocketEvent, Error>> {
let iter_stream = iter( match updates {
updates.into_iter().map(|u| Ok(SocketEvent::Update(Event::from(u)))) SocketUpdate::Update(updates) => {
); let iter_stream = iter(
iter_stream.boxed() updates
.into_iter()
.map(|u| Ok(SocketEvent::Update(Event::from(u)))),
);
iter_stream.boxed()
}
SocketUpdate::Pong => iter(std::iter::once(Ok(SocketEvent::Pong))).boxed(),
} }
SocketUpdate::Pong => { },
iter(std::iter::once(Ok(SocketEvent::Pong))).boxed() )
}
}
})
.try_flatten() .try_flatten()
.boxed(); .boxed();
(stream, sink) (stream, sink)
} }
@@ -212,9 +224,7 @@ impl Stream for ResponseStream {
type Item = Result<Bytes, Error>; type Item = Result<Bytes, Error>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.body self.body.poll_next_unpin(cx).map_err(Error::HTTPError)
.poll_next_unpin(cx)
.map_err(Error::HTTPError)
} }
} }
@@ -328,10 +338,10 @@ impl<K: AuthenticationStore + Send + Sync> APIInterface for HTTPAPIClient<K> {
guid: String, guid: String,
} }
// TODO: We can still use Body::wrap_stream here, but we need to make sure to plumb the CONTENT_LENGTH header, // TODO: We can still use Body::wrap_stream here, but we need to make sure to plumb the CONTENT_LENGTH header,
// otherwise CocoaHTTPServer will crash because of a bug. // otherwise CocoaHTTPServer will crash because of a bug.
// //
// See ff03e73758f30c081a9319a8c04025cba69b8393 for what this was like before. // See ff03e73758f30c081a9319a8c04025cba69b8393 for what this was like before.
let mut bytes = Vec::new(); let mut bytes = Vec::new();
data.read_to_end(&mut bytes) data.read_to_end(&mut bytes)
.await .await
@@ -578,7 +588,11 @@ impl<K: AuthenticationStore + Send + Sync> HTTPAPIClient<K> {
_ => { _ => {
let status = response.status(); let status = response.status();
let body_str = hyper::body::to_bytes(response.into_body()).await?; let body_str = hyper::body::to_bytes(response.into_body()).await?;
let message = format!("Request failed ({:}). Response body: {:?}", status, String::from_utf8_lossy(&body_str)); let message = format!(
"Request failed ({:}). Response body: {:?}",
status,
String::from_utf8_lossy(&body_str)
);
return Err(Error::ClientError(message)); return Err(Error::ClientError(message));
} }
} }

View File

@@ -2,8 +2,7 @@ use super::conversation::Conversation;
use super::message::Message; use super::message::Message;
use serde::Deserialize; use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize, Default)]
#[derive(Default)]
pub struct UpdateItem { pub struct UpdateItem {
#[serde(rename = "messageSequenceNumber")] #[serde(rename = "messageSequenceNumber")]
pub seq: u64, pub seq: u64,
@@ -17,4 +16,3 @@ pub struct UpdateItem {
#[serde(default)] #[serde(default)]
pub pong: bool, pub pong: bool,
} }

View File

@@ -56,7 +56,12 @@ impl EventSocket for TestEventSocket {
type EventStream = BoxStream<'static, Result<SocketEvent, TestError>>; type EventStream = BoxStream<'static, Result<SocketEvent, TestError>>;
type UpdateStream = BoxStream<'static, Result<SocketUpdate, TestError>>; type UpdateStream = BoxStream<'static, Result<SocketUpdate, TestError>>;
async fn events(self) -> (Self::EventStream, impl Sink<SinkMessage, Error = Self::Error>) { async fn events(
self,
) -> (
Self::EventStream,
impl Sink<SinkMessage, Error = Self::Error>,
) {
( (
futures_util::stream::iter(self.events.into_iter().map(Ok)).boxed(), futures_util::stream::iter(self.events.into_iter().map(Ok)).boxed(),
futures_util::sink::sink(), futures_util::sink::sink(),

View File

@@ -114,11 +114,11 @@ impl AttachmentStore {
store_path: &PathBuf, store_path: &PathBuf,
database: &mut Arc<Mutex<Database>>, database: &mut Arc<Mutex<Database>>,
daemon_event_sink: &Sender<DaemonEvent>, daemon_event_sink: &Sender<DaemonEvent>,
guid: &String, guid: &String,
preview: bool preview: bool,
) -> Result<()> { ) -> Result<()> {
let attachment = Self::get_attachment_impl(store_path, guid); let attachment = Self::get_attachment_impl(store_path, guid);
if attachment.is_downloaded(preview) { if attachment.is_downloaded(preview) {
log::info!(target: target::ATTACHMENTS, "Attachment already downloaded: {}", attachment.guid); log::info!(target: target::ATTACHMENTS, "Attachment already downloaded: {}", attachment.guid);
return Err(AttachmentStoreError::AttachmentAlreadyDownloaded.into()); return Err(AttachmentStoreError::AttachmentAlreadyDownloaded.into());
@@ -144,13 +144,16 @@ impl AttachmentStore {
while let Some(Ok(data)) = stream.next().await { while let Some(Ok(data)) = stream.next().await {
writer.write(data.as_ref())?; writer.write(data.as_ref())?;
} }
// Flush and sync the temporary file before moving // Flush and sync the temporary file before moving
writer.flush()?; writer.flush()?;
file.sync_all()?; file.sync_all()?;
// Atomically move the temporary file to the final location // Atomically move the temporary file to the final location
std::fs::rename(&temporary_path, &attachment.get_path_for_preview_scratch(preview, false))?; std::fs::rename(
&temporary_path,
&attachment.get_path_for_preview_scratch(preview, false),
)?;
log::info!(target: target::ATTACHMENTS, "Completed download for attachment: {}", attachment.guid); log::info!(target: target::ATTACHMENTS, "Completed download for attachment: {}", attachment.guid);
@@ -175,12 +178,12 @@ impl AttachmentStore {
let uploads_path = store_path.join("uploads"); let uploads_path = store_path.join("uploads");
std::fs::create_dir_all(&uploads_path).unwrap(); std::fs::create_dir_all(&uploads_path).unwrap();
// First, copy the file to the store path, under /uploads/. // First, copy the file to the store path, under /uploads/.
log::trace!(target: target::ATTACHMENTS, "Copying attachment to uploads directory: {}", uploads_path.display()); log::trace!(target: target::ATTACHMENTS, "Copying attachment to uploads directory: {}", uploads_path.display());
let temporary_path = uploads_path.join(incoming_path.file_name().unwrap()); let temporary_path = uploads_path.join(incoming_path.file_name().unwrap());
std::fs::copy(incoming_path, &temporary_path).unwrap(); std::fs::copy(incoming_path, &temporary_path).unwrap();
// Open file handle to the temporary file, // Open file handle to the temporary file,
log::trace!(target: target::ATTACHMENTS, "Opening stream to temporary file: {}", temporary_path.display()); log::trace!(target: target::ATTACHMENTS, "Opening stream to temporary file: {}", temporary_path.display());
let file = File::open(&temporary_path).await?; let file = File::open(&temporary_path).await?;
let reader: BufReader<File> = BufReader::new(file); let reader: BufReader<File> = BufReader::new(file);
@@ -189,7 +192,7 @@ impl AttachmentStore {
let filename = incoming_path.file_name().unwrap().to_str().unwrap(); let filename = incoming_path.file_name().unwrap().to_str().unwrap();
log::trace!(target: target::ATTACHMENTS, "Uploading attachment to server: {}", &filename); log::trace!(target: target::ATTACHMENTS, "Uploading attachment to server: {}", &filename);
let mut client = Daemon::get_client_impl(database).await?; let mut client = Daemon::get_client_impl(database).await?;
let metadata = std::fs::metadata(&temporary_path)?; let metadata = std::fs::metadata(&temporary_path)?;
let size = metadata.len(); let size = metadata.len();
let guid = client.upload_attachment(reader, filename, size).await?; let guid = client.upload_attachment(reader, filename, size).await?;
@@ -220,7 +223,7 @@ impl AttachmentStore {
let daemon_event_sink = self.daemon_event_sink.clone(); let daemon_event_sink = self.daemon_event_sink.clone();
let _guid = guid.clone(); let _guid = guid.clone();
// Spawn a new task here so we don't block incoming queue events. // Spawn a new task here so we don't block incoming queue events.
tokio::spawn(async move { tokio::spawn(async move {
let result = Self::download_attachment_impl( let result = Self::download_attachment_impl(
&store_path, &store_path,

View File

@@ -224,7 +224,7 @@ impl Daemon {
Event::UpdateStreamReconnected => { Event::UpdateStreamReconnected => {
log::info!(target: target::UPDATES, "Update stream reconnected"); log::info!(target: target::UPDATES, "Update stream reconnected");
// The ui client will respond differently, but we'll almost certainly want to do a sync-list in response to this. // The ui client will respond differently, but we'll almost certainly want to do a sync-list in response to this.
self.spawn_conversation_list_sync(); self.spawn_conversation_list_sync();
// Send signal to the client that the update stream has been reconnected. // Send signal to the client that the update stream has been reconnected.
@@ -267,12 +267,14 @@ impl Daemon {
self.spawn_conversation_list_sync(); self.spawn_conversation_list_sync();
// Also restart the update monitor. // Also restart the update monitor.
if let Err(e) = self.update_monitor_command_tx if let Err(e) = self
.update_monitor_command_tx
.as_ref() .as_ref()
.unwrap() .unwrap()
.try_send(UpdateMonitorCommand::Restart) { .try_send(UpdateMonitorCommand::Restart)
log::warn!(target: target::UPDATES, "Failed to send restart command to update monitor: {}", e); {
} log::warn!(target: target::UPDATES, "Failed to send restart command to update monitor: {}", e);
}
} }
reply.send(()).unwrap(); reply.send(()).unwrap();
@@ -428,7 +430,12 @@ impl Daemon {
.await .await
} }
async fn enqueue_outgoing_message(&mut self, text: String, conversation_id: String, attachment_guids: Vec<String>) -> Uuid { async fn enqueue_outgoing_message(
&mut self,
text: String,
conversation_id: String,
attachment_guids: Vec<String>,
) -> Uuid {
let conversation_id = conversation_id.clone(); let conversation_id = conversation_id.clone();
let outgoing_message = OutgoingMessage::builder() let outgoing_message = OutgoingMessage::builder()
.text(text) .text(text)
@@ -553,11 +560,12 @@ impl Daemon {
.await?; .await?;
// Filter messages that have an empty body, or a body that is just whitespace. // Filter messages that have an empty body, or a body that is just whitespace.
// This is a workaround for a bug in the server where it returns messages with an empty body, which is usually // This is a workaround for a bug in the server where it returns messages with an empty body, which is usually
// the typing indicator or stuff like that. In the future, we need to move to ChatItems instead of Messages. // the typing indicator or stuff like that. In the future, we need to move to ChatItems instead of Messages.
let insertable_messages: Vec<kordophone::model::Message> = messages let insertable_messages: Vec<kordophone::model::Message> = messages
.into_iter() .into_iter()
.filter(|m| !m.text.is_empty() && !m.text.trim().is_empty()).collect(); .filter(|m| !m.text.is_empty() && !m.text.trim().is_empty())
.collect();
let db_messages: Vec<kordophone_db::models::Message> = insertable_messages let db_messages: Vec<kordophone_db::models::Message> = insertable_messages
.into_iter() .into_iter()

View File

@@ -30,7 +30,8 @@ impl Attachment {
pub fn get_path_for_preview_scratch(&self, preview: bool, scratch: bool) -> PathBuf { pub fn get_path_for_preview_scratch(&self, preview: bool, scratch: bool) -> PathBuf {
let extension = if preview { "preview" } else { "full" }; let extension = if preview { "preview" } else { "full" };
if scratch { if scratch {
self.base_path.with_extension(format!("{}.download", extension)) self.base_path
.with_extension(format!("{}.download", extension))
} else { } else {
self.base_path.with_extension(extension) self.base_path.with_extension(extension)
} }

View File

@@ -40,7 +40,7 @@ impl UpdateMonitor {
event_sender, event_sender,
last_sync_times: HashMap::new(), last_sync_times: HashMap::new(),
update_seq: None, update_seq: None,
first_connection: false, // optimistic assumption that we're not reconnecting the first time. first_connection: false, // optimistic assumption that we're not reconnecting the first time.
command_tx: Some(command_tx), command_tx: Some(command_tx),
command_rx, command_rx,
} }
@@ -50,10 +50,7 @@ impl UpdateMonitor {
self.command_tx.take().unwrap() self.command_tx.take().unwrap()
} }
async fn send_event<T>( async fn send_event<T>(&self, make_event: impl FnOnce(Reply<T>) -> Event) -> DaemonResult<T> {
&self,
make_event: impl FnOnce(Reply<T>) -> Event,
) -> DaemonResult<T> {
let (reply_tx, reply_rx) = tokio::sync::oneshot::channel(); let (reply_tx, reply_rx) = tokio::sync::oneshot::channel();
self.event_sender self.event_sender
.send(make_event(reply_tx)) .send(make_event(reply_tx))

View File

@@ -10,10 +10,10 @@ pub mod interface {
include!(concat!(env!("OUT_DIR"), "/kordophone-server.rs")); include!(concat!(env!("OUT_DIR"), "/kordophone-server.rs"));
pub mod signals { pub mod signals {
pub use crate::interface::NetBuzzertKordophoneRepositoryConversationsUpdated as ConversationsUpdated;
pub use crate::interface::NetBuzzertKordophoneRepositoryMessagesUpdated as MessagesUpdated;
pub use crate::interface::NetBuzzertKordophoneRepositoryAttachmentDownloadCompleted as AttachmentDownloadCompleted; pub use crate::interface::NetBuzzertKordophoneRepositoryAttachmentDownloadCompleted as AttachmentDownloadCompleted;
pub use crate::interface::NetBuzzertKordophoneRepositoryAttachmentUploadCompleted as AttachmentUploadCompleted; pub use crate::interface::NetBuzzertKordophoneRepositoryAttachmentUploadCompleted as AttachmentUploadCompleted;
pub use crate::interface::NetBuzzertKordophoneRepositoryConversationsUpdated as ConversationsUpdated;
pub use crate::interface::NetBuzzertKordophoneRepositoryMessagesUpdated as MessagesUpdated;
pub use crate::interface::NetBuzzertKordophoneRepositoryUpdateStreamReconnected as UpdateStreamReconnected; pub use crate::interface::NetBuzzertKordophoneRepositoryUpdateStreamReconnected as UpdateStreamReconnected;
} }
} }

View File

@@ -128,11 +128,11 @@ impl DbusRepository for ServerImpl {
let mut map = arg::PropMap::new(); let mut map = arg::PropMap::new();
map.insert("id".into(), arg::Variant(Box::new(msg.id))); map.insert("id".into(), arg::Variant(Box::new(msg.id)));
// xxx: Remove the attachment placeholder here. // xxx: Remove the attachment placeholder here.
// This is not the ideal place to do this, but once we start using ChatItems instead of IMMessages // This is not the ideal place to do this, but once we start using ChatItems instead of IMMessages
// from the server, we shouldn't be seeing these placeholders. // from the server, we shouldn't be seeing these placeholders.
let text = msg.text.replace("\u{FFFC}", ""); let text = msg.text.replace("\u{FFFC}", "");
map.insert("text".into(), arg::Variant(Box::new(text))); map.insert("text".into(), arg::Variant(Box::new(text)));
map.insert( map.insert(
"date".into(), "date".into(),
@@ -272,12 +272,9 @@ impl DbusRepository for ServerImpl {
self.send_event_sync(|r| Event::DownloadAttachment(attachment_id, preview, r)) self.send_event_sync(|r| Event::DownloadAttachment(attachment_id, preview, r))
} }
fn upload_attachment( fn upload_attachment(&mut self, path: String) -> Result<String, dbus::MethodErr> {
&mut self,
path: String,
) -> Result<String, dbus::MethodErr> {
use std::path::PathBuf; use std::path::PathBuf;
let path = PathBuf::from(path); let path = PathBuf::from(path);
self.send_event_sync(|r| Event::UploadAttachment(path, r)) self.send_event_sync(|r| Event::UploadAttachment(path, r))
} }

View File

@@ -100,9 +100,15 @@ async fn main() {
} }
Signal::AttachmentDownloaded(attachment_id) => { Signal::AttachmentDownloaded(attachment_id) => {
log::debug!("Sending signal: AttachmentDownloaded for attachment {}", attachment_id); log::debug!(
"Sending signal: AttachmentDownloaded for attachment {}",
attachment_id
);
dbus_registry dbus_registry
.send_signal(interface::OBJECT_PATH, DbusSignals::AttachmentDownloadCompleted { attachment_id }) .send_signal(
interface::OBJECT_PATH,
DbusSignals::AttachmentDownloadCompleted { attachment_id },
)
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
log::error!("Failed to send signal"); log::error!("Failed to send signal");
0 0
@@ -110,9 +116,19 @@ async fn main() {
} }
Signal::AttachmentUploaded(upload_guid, attachment_guid) => { Signal::AttachmentUploaded(upload_guid, attachment_guid) => {
log::debug!("Sending signal: AttachmentUploaded for upload {}, attachment {}", upload_guid, attachment_guid); log::debug!(
"Sending signal: AttachmentUploaded for upload {}, attachment {}",
upload_guid,
attachment_guid
);
dbus_registry dbus_registry
.send_signal(interface::OBJECT_PATH, DbusSignals::AttachmentUploadCompleted { upload_guid, attachment_guid }) .send_signal(
interface::OBJECT_PATH,
DbusSignals::AttachmentUploadCompleted {
upload_guid,
attachment_guid,
},
)
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
log::error!("Failed to send signal"); log::error!("Failed to send signal");
0 0
@@ -122,7 +138,10 @@ async fn main() {
Signal::UpdateStreamReconnected => { Signal::UpdateStreamReconnected => {
log::debug!("Sending signal: UpdateStreamReconnected"); log::debug!("Sending signal: UpdateStreamReconnected");
dbus_registry dbus_registry
.send_signal(interface::OBJECT_PATH, DbusSignals::UpdateStreamReconnected {}) .send_signal(
interface::OBJECT_PATH,
DbusSignals::UpdateStreamReconnected {},
)
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
log::error!("Failed to send signal"); log::error!("Failed to send signal");
0 0

View File

@@ -113,19 +113,17 @@ impl ClientCli {
let (mut stream, _) = socket.events().await; let (mut stream, _) = socket.events().await;
while let Some(Ok(socket_event)) = stream.next().await { while let Some(Ok(socket_event)) = stream.next().await {
match socket_event { match socket_event {
SocketEvent::Update(event) => { SocketEvent::Update(event) => match event.data {
match event.data { EventData::ConversationChanged(conversation) => {
EventData::ConversationChanged(conversation) => { println!("Conversation changed: {}", conversation.guid);
println!("Conversation changed: {}", conversation.guid);
}
EventData::MessageReceived(conversation, message) => {
println!(
"Message received: msg: {} conversation: {}",
message.guid, conversation.guid
);
}
} }
} EventData::MessageReceived(conversation, message) => {
println!(
"Message received: msg: {} conversation: {}",
message.guid, conversation.guid
);
}
},
SocketEvent::Pong => { SocketEvent::Pong => {
println!("Pong"); println!("Pong");
} }

View File

@@ -54,14 +54,10 @@ pub enum Commands {
}, },
/// Downloads an attachment from the server to the attachment store. Returns the path to the attachment. /// Downloads an attachment from the server to the attachment store. Returns the path to the attachment.
DownloadAttachment { DownloadAttachment { attachment_id: String },
attachment_id: String,
},
/// Uploads an attachment to the server, returns upload guid. /// Uploads an attachment to the server, returns upload guid.
UploadAttachment { UploadAttachment { path: String },
path: String,
},
} }
#[derive(Subcommand)] #[derive(Subcommand)]
@@ -100,7 +96,9 @@ impl Commands {
text, text,
} => client.enqueue_outgoing_message(conversation_id, text).await, } => client.enqueue_outgoing_message(conversation_id, text).await,
Commands::UploadAttachment { path } => client.upload_attachment(path).await, Commands::UploadAttachment { path } => client.upload_attachment(path).await,
Commands::DownloadAttachment { attachment_id } => client.download_attachment(attachment_id).await, Commands::DownloadAttachment { attachment_id } => {
client.download_attachment(attachment_id).await
}
} }
} }
} }
@@ -178,8 +176,12 @@ impl DaemonCli {
text: String, text: String,
) -> Result<()> { ) -> Result<()> {
let attachment_guids: Vec<&str> = vec![]; let attachment_guids: Vec<&str> = vec![];
let outgoing_message_id = let outgoing_message_id = KordophoneRepository::send_message(
KordophoneRepository::send_message(&self.proxy(), &conversation_id, &text, attachment_guids)?; &self.proxy(),
&conversation_id,
&text,
attachment_guids,
)?;
println!("Outgoing message ID: {}", outgoing_message_id); println!("Outgoing message ID: {}", outgoing_message_id);
Ok(()) Ok(())
} }
@@ -244,7 +246,8 @@ impl DaemonCli {
KordophoneRepository::download_attachment(&self.proxy(), &attachment_id, false)?; KordophoneRepository::download_attachment(&self.proxy(), &attachment_id, false)?;
// Get attachment info. // Get attachment info.
let attachment_info = KordophoneRepository::get_attachment_info(&self.proxy(), &attachment_id)?; let attachment_info =
KordophoneRepository::get_attachment_info(&self.proxy(), &attachment_id)?;
let (path, preview_path, downloaded, preview_downloaded) = attachment_info; let (path, preview_path, downloaded, preview_downloaded) = attachment_info;
if downloaded { if downloaded {
@@ -256,14 +259,18 @@ impl DaemonCli {
// Attach to the signal that the attachment has been downloaded. // Attach to the signal that the attachment has been downloaded.
let _id = self.proxy().match_signal( let _id = self.proxy().match_signal(
move |h: dbus_interface::NetBuzzertKordophoneRepositoryAttachmentDownloadCompleted, _: &Connection, _: &dbus::message::Message| { move |h: dbus_interface::NetBuzzertKordophoneRepositoryAttachmentDownloadCompleted,
_: &Connection,
_: &dbus::message::Message| {
println!("Signal: Attachment downloaded: {}", path); println!("Signal: Attachment downloaded: {}", path);
std::process::exit(0); std::process::exit(0);
}, },
); );
let _id = self.proxy().match_signal( let _id = self.proxy().match_signal(
|h: dbus_interface::NetBuzzertKordophoneRepositoryAttachmentDownloadFailed, _: &Connection, _: &dbus::message::Message| { |h: dbus_interface::NetBuzzertKordophoneRepositoryAttachmentDownloadFailed,
_: &Connection,
_: &dbus::message::Message| {
println!("Signal: Attachment download failed: {}", h.attachment_id); println!("Signal: Attachment download failed: {}", h.attachment_id);
std::process::exit(1); std::process::exit(1);
}, },