cargo fmt
This commit is contained in:
@@ -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(())
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user