Add plumbing for new message/reply through core, gtk, and osx
This commit is contained in:
@@ -57,9 +57,7 @@ pub mod api_interface {
|
|||||||
async fn test_delete_conversation() {
|
async fn test_delete_conversation() {
|
||||||
let mut client = TestClient::new();
|
let mut client = TestClient::new();
|
||||||
|
|
||||||
let test_convo = Conversation::builder()
|
let test_convo = Conversation::builder().display_name("Delete Me").build();
|
||||||
.display_name("Delete Me")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
client.conversations.push(test_convo.clone());
|
client.conversations.push(test_convo.clone());
|
||||||
|
|
||||||
|
|||||||
@@ -114,11 +114,23 @@ impl DaemonClient for DBusClient {
|
|||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_message(&mut self, conversation_id: String, text: String) -> Result<Option<String>> {
|
fn reply(&mut self, conversation_id: String, text: String) -> Result<Option<String>> {
|
||||||
let attachment_guids: Vec<&str> = vec![];
|
let attachment_guids: Vec<&str> = vec![];
|
||||||
let outgoing_id = KordophoneRepository::send_message(
|
let outgoing_id =
|
||||||
|
KordophoneRepository::reply(&self.proxy(), &conversation_id, &text, attachment_guids)?;
|
||||||
|
Ok(Some(outgoing_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_conversation(
|
||||||
|
&mut self,
|
||||||
|
handle_ids: Vec<String>,
|
||||||
|
text: String,
|
||||||
|
) -> Result<Option<String>> {
|
||||||
|
let attachment_guids: Vec<&str> = vec![];
|
||||||
|
let handle_ids: Vec<&str> = handle_ids.iter().map(String::as_str).collect();
|
||||||
|
let outgoing_id = KordophoneRepository::new_conversation(
|
||||||
&self.proxy(),
|
&self.proxy(),
|
||||||
&conversation_id,
|
handle_ids,
|
||||||
&text,
|
&text,
|
||||||
attachment_guids,
|
attachment_guids,
|
||||||
)?;
|
)?;
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ impl DaemonClient for XpcClient {
|
|||||||
Ok(messages)
|
Ok(messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_message(&mut self, conversation_id: String, text: String) -> Result<Option<String>> {
|
fn reply(&mut self, conversation_id: String, text: String) -> Result<Option<String>> {
|
||||||
let mut args = HashMap::new();
|
let mut args = HashMap::new();
|
||||||
args.insert(
|
args.insert(
|
||||||
Self::key("conversation_id"),
|
Self::key("conversation_id"),
|
||||||
@@ -211,7 +211,34 @@ impl DaemonClient for XpcClient {
|
|||||||
|
|
||||||
let reply = self
|
let reply = self
|
||||||
.transport
|
.transport
|
||||||
.send_with_reply(Self::request("SendMessage", Some(args)));
|
.send_with_reply(Self::request("Reply", Some(args)));
|
||||||
|
let Message::Dictionary(map) = reply else {
|
||||||
|
anyhow::bail!("Unexpected send response");
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self::get_string(&map, "uuid"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_conversation(
|
||||||
|
&mut self,
|
||||||
|
handle_ids: Vec<String>,
|
||||||
|
text: String,
|
||||||
|
) -> Result<Option<String>> {
|
||||||
|
let mut args = HashMap::new();
|
||||||
|
args.insert(
|
||||||
|
Self::key("handle_ids"),
|
||||||
|
Message::Array(
|
||||||
|
handle_ids
|
||||||
|
.into_iter()
|
||||||
|
.map(|handle_id| Message::String(Self::key(&handle_id)))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
args.insert(Self::key("text"), Message::String(Self::key(&text)));
|
||||||
|
|
||||||
|
let reply = self
|
||||||
|
.transport
|
||||||
|
.send_with_reply(Self::request("NewConversation", Some(args)));
|
||||||
let Message::Dictionary(map) = reply else {
|
let Message::Dictionary(map) = reply else {
|
||||||
anyhow::bail!("Unexpected send response");
|
anyhow::bail!("Unexpected send response");
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,10 +24,14 @@ pub enum Request {
|
|||||||
RefreshMessages {
|
RefreshMessages {
|
||||||
conversation_id: String,
|
conversation_id: String,
|
||||||
},
|
},
|
||||||
SendMessage {
|
Reply {
|
||||||
conversation_id: String,
|
conversation_id: String,
|
||||||
text: String,
|
text: String,
|
||||||
},
|
},
|
||||||
|
NewConversation {
|
||||||
|
handle_ids: Vec<String>,
|
||||||
|
text: String,
|
||||||
|
},
|
||||||
MarkRead {
|
MarkRead {
|
||||||
conversation_id: String,
|
conversation_id: String,
|
||||||
},
|
},
|
||||||
@@ -42,8 +46,8 @@ pub enum Event {
|
|||||||
conversation_id: String,
|
conversation_id: String,
|
||||||
messages: Vec<ChatMessage>,
|
messages: Vec<ChatMessage>,
|
||||||
},
|
},
|
||||||
MessageSent {
|
MessageQueued {
|
||||||
conversation_id: String,
|
conversation_id: Option<String>,
|
||||||
outgoing_id: Option<String>,
|
outgoing_id: Option<String>,
|
||||||
},
|
},
|
||||||
MarkedRead,
|
MarkedRead,
|
||||||
@@ -80,33 +84,38 @@ pub fn spawn_worker(
|
|||||||
loop {
|
loop {
|
||||||
match request_rx.recv_timeout(Duration::from_millis(100)) {
|
match request_rx.recv_timeout(Duration::from_millis(100)) {
|
||||||
Ok(req) => {
|
Ok(req) => {
|
||||||
let res =
|
let res = match req {
|
||||||
match req {
|
Request::RefreshConversations => {
|
||||||
Request::RefreshConversations => {
|
client.get_conversations(200, 0).map(Event::Conversations)
|
||||||
client.get_conversations(200, 0).map(Event::Conversations)
|
}
|
||||||
}
|
Request::RefreshMessages { conversation_id } => client
|
||||||
Request::RefreshMessages { conversation_id } => client
|
.get_messages(conversation_id.clone(), None)
|
||||||
.get_messages(conversation_id.clone(), None)
|
.map(|messages| Event::Messages {
|
||||||
.map(|messages| Event::Messages {
|
|
||||||
conversation_id,
|
|
||||||
messages,
|
|
||||||
}),
|
|
||||||
Request::SendMessage {
|
|
||||||
conversation_id,
|
conversation_id,
|
||||||
text,
|
messages,
|
||||||
} => client.send_message(conversation_id.clone(), text).map(
|
}),
|
||||||
|outgoing_id| Event::MessageSent {
|
Request::Reply {
|
||||||
conversation_id,
|
conversation_id,
|
||||||
outgoing_id,
|
text,
|
||||||
},
|
} => client
|
||||||
),
|
.reply(conversation_id.clone(), text)
|
||||||
Request::MarkRead { conversation_id } => client
|
.map(|outgoing_id| Event::MessageQueued {
|
||||||
.mark_conversation_as_read(conversation_id.clone())
|
conversation_id: Some(conversation_id),
|
||||||
.map(|_| Event::MarkedRead),
|
outgoing_id,
|
||||||
Request::SyncConversation { conversation_id } => client
|
}),
|
||||||
.sync_conversation(conversation_id.clone())
|
Request::NewConversation { handle_ids, text } => client
|
||||||
.map(|_| Event::ConversationSyncTriggered { conversation_id }),
|
.new_conversation(handle_ids, text)
|
||||||
};
|
.map(|outgoing_id| Event::MessageQueued {
|
||||||
|
conversation_id: None,
|
||||||
|
outgoing_id,
|
||||||
|
}),
|
||||||
|
Request::MarkRead { conversation_id } => client
|
||||||
|
.mark_conversation_as_read(conversation_id.clone())
|
||||||
|
.map(|_| Event::MarkedRead),
|
||||||
|
Request::SyncConversation { conversation_id } => client
|
||||||
|
.sync_conversation(conversation_id.clone())
|
||||||
|
.map(|_| Event::ConversationSyncTriggered { conversation_id }),
|
||||||
|
};
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(evt) => {
|
Ok(evt) => {
|
||||||
@@ -135,7 +144,9 @@ pub(crate) trait DaemonClient {
|
|||||||
conversation_id: String,
|
conversation_id: String,
|
||||||
last_message_id: Option<String>,
|
last_message_id: Option<String>,
|
||||||
) -> Result<Vec<ChatMessage>>;
|
) -> Result<Vec<ChatMessage>>;
|
||||||
fn send_message(&mut self, conversation_id: String, text: String) -> Result<Option<String>>;
|
fn reply(&mut self, conversation_id: String, text: String) -> Result<Option<String>>;
|
||||||
|
fn new_conversation(&mut self, handle_ids: Vec<String>, text: String)
|
||||||
|
-> Result<Option<String>>;
|
||||||
fn mark_conversation_as_read(&mut self, conversation_id: String) -> Result<()>;
|
fn mark_conversation_as_read(&mut self, conversation_id: String) -> Result<()>;
|
||||||
fn sync_conversation(&mut self, conversation_id: String) -> Result<()>;
|
fn sync_conversation(&mut self, conversation_id: String) -> Result<()>;
|
||||||
fn install_signal_handlers(&mut self, _event_tx: mpsc::Sender<Event>) -> Result<()> {
|
fn install_signal_handlers(&mut self, _event_tx: mpsc::Sender<Event>) -> Result<()> {
|
||||||
|
|||||||
@@ -83,7 +83,7 @@
|
|||||||
</arg>
|
</arg>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
<method name="SendMessage">
|
<method name="Reply">
|
||||||
<arg type="s" name="conversation_id" direction="in"/>
|
<arg type="s" name="conversation_id" direction="in"/>
|
||||||
<arg type="s" name="text" direction="in"/>
|
<arg type="s" name="text" direction="in"/>
|
||||||
<arg type="as" name="attachment_guids" direction="in"/>
|
<arg type="as" name="attachment_guids" direction="in"/>
|
||||||
@@ -91,9 +91,28 @@
|
|||||||
<arg type="s" name="outgoing_message_id" direction="out"/>
|
<arg type="s" name="outgoing_message_id" direction="out"/>
|
||||||
|
|
||||||
<annotation name="org.freedesktop.DBus.DocString"
|
<annotation name="org.freedesktop.DBus.DocString"
|
||||||
value="Sends a message to the server. Returns the outgoing message ID.
|
value="Replies to an existing conversation. Returns the outgoing message ID.
|
||||||
Arguments:
|
Arguments:
|
||||||
- conversation_id: The ID of the conversation to send the message to.
|
- conversation_id: The ID of the conversation to reply to.
|
||||||
|
- text: The text of the message to send.
|
||||||
|
- attachment_guids: The GUIDs of the attachments to send.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- outgoing_message_id: The ID of the outgoing message.
|
||||||
|
"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="NewConversation">
|
||||||
|
<arg type="as" name="handle_ids" direction="in"/>
|
||||||
|
<arg type="s" name="text" direction="in"/>
|
||||||
|
<arg type="as" name="attachment_guids" direction="in"/>
|
||||||
|
|
||||||
|
<arg type="s" name="outgoing_message_id" direction="out"/>
|
||||||
|
|
||||||
|
<annotation name="org.freedesktop.DBus.DocString"
|
||||||
|
value="Sends a message to a new conversation identified by resolved handles.
|
||||||
|
Arguments:
|
||||||
|
- handle_ids: The resolved handles for the new conversation.
|
||||||
- text: The text of the message to send.
|
- text: The text of the message to send.
|
||||||
- attachment_guids: The GUIDs of the attachments to send.
|
- attachment_guids: The GUIDs of the attachments to send.
|
||||||
|
|
||||||
|
|||||||
@@ -53,13 +53,21 @@ pub enum Event {
|
|||||||
/// - last_message_id: (optional) The ID of the last message to get. If None, all messages are returned.
|
/// - last_message_id: (optional) The ID of the last message to get. If None, all messages are returned.
|
||||||
GetMessages(String, Option<String>, Reply<Vec<Message>>),
|
GetMessages(String, Option<String>, Reply<Vec<Message>>),
|
||||||
|
|
||||||
/// Enqueues a message to be sent to the server.
|
/// Enqueues a reply to an existing conversation.
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
/// - conversation_id: The ID of the conversation to send the message to.
|
/// - conversation_id: The ID of the conversation to send the message to.
|
||||||
/// - text: The text of the message to send.
|
/// - text: The text of the message to send.
|
||||||
/// - attachment_guids: The GUIDs of the attachments to send.
|
/// - attachment_guids: The GUIDs of the attachments to send.
|
||||||
/// - reply: The outgoing message ID (not the server-assigned message ID).
|
/// - reply: The outgoing message ID (not the server-assigned message ID).
|
||||||
SendMessage(String, String, Vec<String>, Reply<Uuid>),
|
Reply(String, String, Vec<String>, Reply<Uuid>),
|
||||||
|
|
||||||
|
/// Enqueues a message to one or more resolved handles.
|
||||||
|
/// Parameters:
|
||||||
|
/// - handle_ids: The resolved handle IDs for the new conversation.
|
||||||
|
/// - text: The text of the message to send.
|
||||||
|
/// - attachment_guids: The GUIDs of the attachments to send.
|
||||||
|
/// - reply: The outgoing message ID (not the server-assigned message ID).
|
||||||
|
NewConversation(Vec<String>, String, Vec<String>, Reply<Uuid>),
|
||||||
|
|
||||||
/// Notifies the daemon that a message has been sent.
|
/// Notifies the daemon that a message has been sent.
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ use kordophone_db::{
|
|||||||
|
|
||||||
use kordophone::api::http_client::HTTPAPIClient;
|
use kordophone::api::http_client::HTTPAPIClient;
|
||||||
use kordophone::api::APIInterface;
|
use kordophone::api::APIInterface;
|
||||||
use kordophone::model::outgoing_message::OutgoingMessage;
|
use kordophone::model::outgoing_message::{OutgoingMessage, OutgoingMessageTarget};
|
||||||
use kordophone::model::{ConversationID, MessageID};
|
use kordophone::model::{ConversationID, MessageID};
|
||||||
|
|
||||||
mod update_monitor;
|
mod update_monitor;
|
||||||
@@ -330,10 +330,14 @@ impl Daemon {
|
|||||||
let _ = reply.send(());
|
let _ = reply.send(());
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::SendMessage(conversation_id, text, attachment_guids, reply) => {
|
Event::Reply(conversation_id, text, attachment_guids, reply) => {
|
||||||
let conversation_id = conversation_id.clone();
|
let conversation_id = conversation_id.clone();
|
||||||
let uuid = self
|
let uuid = self
|
||||||
.enqueue_outgoing_message(text, conversation_id.clone(), attachment_guids)
|
.enqueue_outgoing_message(
|
||||||
|
text,
|
||||||
|
OutgoingMessageTarget::Conversation(conversation_id.clone()),
|
||||||
|
attachment_guids,
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
let _ = reply.send(uuid);
|
let _ = reply.send(uuid);
|
||||||
|
|
||||||
@@ -344,12 +348,52 @@ impl Daemon {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Event::NewConversation(handle_ids, text, attachment_guids, reply) => {
|
||||||
|
let uuid = self
|
||||||
|
.enqueue_outgoing_message(
|
||||||
|
text,
|
||||||
|
OutgoingMessageTarget::Handles(handle_ids),
|
||||||
|
attachment_guids,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let _ = reply.send(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
Event::MessageSent(message, outgoing_message, conversation_id) => {
|
Event::MessageSent(message, outgoing_message, conversation_id) => {
|
||||||
log::info!(target: target::EVENT, "Daemon: message sent: {}", message.id);
|
log::info!(target: target::EVENT, "Daemon: message sent: {}", message.id);
|
||||||
|
|
||||||
|
let conversation_created = match self
|
||||||
|
.ensure_conversation_exists_for_sent_message(
|
||||||
|
&conversation_id,
|
||||||
|
&outgoing_message,
|
||||||
|
&message,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(created) => created,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(
|
||||||
|
target: target::EVENT,
|
||||||
|
"Failed to ensure conversation {} exists for sent message {}: {}",
|
||||||
|
conversation_id,
|
||||||
|
message.id,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if conversation_created {
|
||||||
|
self.signal_sender
|
||||||
|
.send(Signal::ConversationsUpdated)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
// Insert the message into the database.
|
// Insert the message into the database.
|
||||||
log::debug!(target: target::EVENT, "inserting sent message into database: {}", message.id);
|
log::debug!(target: target::EVENT, "inserting sent message into database: {}", message.id);
|
||||||
self.database
|
if let Err(e) = self
|
||||||
|
.database
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.with_repository(|r| {
|
.with_repository(|r| {
|
||||||
@@ -363,13 +407,24 @@ impl Daemon {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
{
|
||||||
|
log::error!(
|
||||||
|
target: target::EVENT,
|
||||||
|
"Failed to persist sent message {} for conversation {}: {}",
|
||||||
|
message.id,
|
||||||
|
conversation_id,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove from outgoing messages.
|
// Remove from outgoing messages.
|
||||||
log::debug!(target: target::EVENT, "Removing message from outgoing messages: {}", outgoing_message.guid);
|
log::debug!(target: target::EVENT, "Removing message from outgoing messages: {}", outgoing_message.guid);
|
||||||
|
for messages in self.outgoing_messages.values_mut() {
|
||||||
|
messages.retain(|m| m.guid != outgoing_message.guid);
|
||||||
|
}
|
||||||
self.outgoing_messages
|
self.outgoing_messages
|
||||||
.get_mut(&conversation_id)
|
.retain(|_, messages| !messages.is_empty());
|
||||||
.map(|messages| messages.retain(|m| m.guid != outgoing_message.guid));
|
|
||||||
|
|
||||||
// Send message updated signal.
|
// Send message updated signal.
|
||||||
self.signal_sender
|
self.signal_sender
|
||||||
@@ -517,24 +572,87 @@ impl Daemon {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn ensure_conversation_exists_for_sent_message(
|
||||||
|
&mut self,
|
||||||
|
conversation_id: &ConversationID,
|
||||||
|
outgoing_message: &OutgoingMessage,
|
||||||
|
message: &Message,
|
||||||
|
) -> Result<bool> {
|
||||||
|
let conversation_exists = self
|
||||||
|
.database
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.with_repository(|r| r.get_conversation_by_guid(conversation_id))
|
||||||
|
.await?
|
||||||
|
.is_some();
|
||||||
|
|
||||||
|
if conversation_exists {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let participants = Self::participants_for_outgoing_message(outgoing_message);
|
||||||
|
let mut builder = Conversation::builder()
|
||||||
|
.guid(conversation_id)
|
||||||
|
.date(message.date)
|
||||||
|
.unread_count(0)
|
||||||
|
.participants(participants);
|
||||||
|
|
||||||
|
if !message.text.trim().is_empty() {
|
||||||
|
builder = builder.last_message_preview(&message.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
let conversation = builder.build();
|
||||||
|
log::info!(
|
||||||
|
target: target::EVENT,
|
||||||
|
"Creating local conversation {} from sent message {}",
|
||||||
|
conversation_id,
|
||||||
|
message.id
|
||||||
|
);
|
||||||
|
|
||||||
|
self.database
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.with_repository(|r| r.insert_conversation(conversation))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn participants_for_outgoing_message(outgoing_message: &OutgoingMessage) -> Vec<DbParticipant> {
|
||||||
|
let handle_ids = match &outgoing_message.target {
|
||||||
|
OutgoingMessageTarget::Conversation(_) => return Vec::new(),
|
||||||
|
OutgoingMessageTarget::Handles(handle_ids) => handle_ids,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut contact_resolver = ContactResolver::new(DefaultContactResolverBackend::default());
|
||||||
|
handle_ids
|
||||||
|
.iter()
|
||||||
|
.map(|handle| DbParticipant::Remote {
|
||||||
|
handle: handle.clone(),
|
||||||
|
contact_id: contact_resolver.resolve_contact_id(handle),
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
async fn enqueue_outgoing_message(
|
async fn enqueue_outgoing_message(
|
||||||
&mut self,
|
&mut self,
|
||||||
text: String,
|
text: String,
|
||||||
conversation_id: String,
|
target: OutgoingMessageTarget,
|
||||||
attachment_guids: Vec<String>,
|
attachment_guids: Vec<String>,
|
||||||
) -> Uuid {
|
) -> Uuid {
|
||||||
let conversation_id = conversation_id.clone();
|
|
||||||
let outgoing_message = OutgoingMessage::builder()
|
let outgoing_message = OutgoingMessage::builder()
|
||||||
.text(text)
|
.text(text)
|
||||||
.conversation_id(conversation_id.clone())
|
.target(target)
|
||||||
.file_transfer_guids(attachment_guids)
|
.file_transfer_guids(attachment_guids)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Keep a record of this so we can provide a consistent model to the client.
|
if let Some(conversation_id) = outgoing_message.conversation_id().cloned() {
|
||||||
self.outgoing_messages
|
// Keep a record of replies so we can provide a consistent model to the client.
|
||||||
.entry(conversation_id)
|
self.outgoing_messages
|
||||||
.or_insert(vec![])
|
.entry(conversation_id)
|
||||||
.push(outgoing_message.clone());
|
.or_insert(vec![])
|
||||||
|
.push(outgoing_message.clone());
|
||||||
|
}
|
||||||
|
|
||||||
let guid = outgoing_message.guid.clone();
|
let guid = outgoing_message.guid.clone();
|
||||||
self.post_office_sink
|
self.post_office_sink
|
||||||
|
|||||||
@@ -388,13 +388,23 @@ impl DbusRepository for DBusAgent {
|
|||||||
self.send_event_sync(Event::DeleteAllConversations)
|
self.send_event_sync(Event::DeleteAllConversations)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_message(
|
fn reply(
|
||||||
&mut self,
|
&mut self,
|
||||||
conversation_id: String,
|
conversation_id: String,
|
||||||
text: String,
|
text: String,
|
||||||
attachment_guids: Vec<String>,
|
attachment_guids: Vec<String>,
|
||||||
) -> Result<String, MethodErr> {
|
) -> Result<String, MethodErr> {
|
||||||
self.send_event_sync(|r| Event::SendMessage(conversation_id, text, attachment_guids, r))
|
self.send_event_sync(|r| Event::Reply(conversation_id, text, attachment_guids, r))
|
||||||
|
.map(|uuid| uuid.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_conversation(
|
||||||
|
&mut self,
|
||||||
|
handle_ids: Vec<String>,
|
||||||
|
text: String,
|
||||||
|
attachment_guids: Vec<String>,
|
||||||
|
) -> Result<String, MethodErr> {
|
||||||
|
self.send_event_sync(|r| Event::NewConversation(handle_ids, text, attachment_guids, r))
|
||||||
.map(|uuid| uuid.to_string())
|
.map(|uuid| uuid.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -254,8 +254,8 @@ pub async fn dispatch(
|
|||||||
Err(e) => DispatchResult::new(make_error_reply("DaemonError", &format!("{}", e))),
|
Err(e) => DispatchResult::new(make_error_reply("DaemonError", &format!("{}", e))),
|
||||||
},
|
},
|
||||||
|
|
||||||
// SendMessage
|
// Reply
|
||||||
"SendMessage" => {
|
"Reply" => {
|
||||||
let args = match get_dictionary_field(root, "arguments") {
|
let args = match get_dictionary_field(root, "arguments") {
|
||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => {
|
None => {
|
||||||
@@ -286,12 +286,64 @@ pub async fn dispatch(
|
|||||||
_ => Vec::new(),
|
_ => Vec::new(),
|
||||||
};
|
};
|
||||||
match agent
|
match agent
|
||||||
.send_event(|r| Event::SendMessage(conversation_id, text, attachment_guids, r))
|
.send_event(|r| Event::Reply(conversation_id, text, attachment_guids, r))
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(uuid) => {
|
Ok(uuid) => {
|
||||||
let mut reply: XpcMap = HashMap::new();
|
let mut reply: XpcMap = HashMap::new();
|
||||||
dict_put_str(&mut reply, "type", "SendMessageResponse");
|
dict_put_str(&mut reply, "type", "ReplyResponse");
|
||||||
|
dict_put_str(&mut reply, "uuid", &uuid.to_string());
|
||||||
|
DispatchResult::new(Message::Dictionary(reply))
|
||||||
|
}
|
||||||
|
Err(e) => DispatchResult::new(make_error_reply("DaemonError", &format!("{}", e))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConversation
|
||||||
|
"NewConversation" => {
|
||||||
|
let args = match get_dictionary_field(root, "arguments") {
|
||||||
|
Some(a) => a,
|
||||||
|
None => {
|
||||||
|
return DispatchResult::new(make_error_reply(
|
||||||
|
"InvalidRequest",
|
||||||
|
"Missing arguments",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let handle_ids: Vec<String> = match args.get(&cstr("handle_ids")) {
|
||||||
|
Some(Message::Array(arr)) => arr
|
||||||
|
.iter()
|
||||||
|
.filter_map(|m| match m {
|
||||||
|
Message::String(s) => Some(s.to_string_lossy().into_owned()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
_ => Vec::new(),
|
||||||
|
};
|
||||||
|
if handle_ids.is_empty() {
|
||||||
|
return DispatchResult::new(make_error_reply(
|
||||||
|
"InvalidRequest",
|
||||||
|
"Missing handle_ids",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let text = dict_get_str(args, "text").unwrap_or_default();
|
||||||
|
let attachment_guids: Vec<String> = match args.get(&cstr("attachment_guids")) {
|
||||||
|
Some(Message::Array(arr)) => arr
|
||||||
|
.iter()
|
||||||
|
.filter_map(|m| match m {
|
||||||
|
Message::String(s) => Some(s.to_string_lossy().into_owned()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
_ => Vec::new(),
|
||||||
|
};
|
||||||
|
match agent
|
||||||
|
.send_event(|r| Event::NewConversation(handle_ids, text, attachment_guids, r))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(uuid) => {
|
||||||
|
let mut reply: XpcMap = HashMap::new();
|
||||||
|
dict_put_str(&mut reply, "type", "NewConversationResponse");
|
||||||
dict_put_str(&mut reply, "uuid", &uuid.to_string());
|
dict_put_str(&mut reply, "uuid", &uuid.to_string());
|
||||||
DispatchResult::new(Message::Dictionary(reply))
|
DispatchResult::new(Message::Dictionary(reply))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,9 @@ impl Commands {
|
|||||||
Commands::Mark { conversation_id } => {
|
Commands::Mark { conversation_id } => {
|
||||||
client.mark_conversation_as_read(conversation_id).await
|
client.mark_conversation_as_read(conversation_id).await
|
||||||
}
|
}
|
||||||
Commands::Delete { conversation_id } => client.delete_conversation(conversation_id).await,
|
Commands::Delete { conversation_id } => {
|
||||||
|
client.delete_conversation(conversation_id).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -211,11 +213,7 @@ impl ClientCli {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_message(
|
async fn send_message(&mut self, target: OutgoingMessageTarget, message: String) -> Result<()> {
|
||||||
&mut self,
|
|
||||||
target: OutgoingMessageTarget,
|
|
||||||
message: String,
|
|
||||||
) -> Result<()> {
|
|
||||||
let outgoing_message = OutgoingMessage::builder()
|
let outgoing_message = OutgoingMessage::builder()
|
||||||
.target(target)
|
.target(target)
|
||||||
.text(message)
|
.text(message)
|
||||||
@@ -255,8 +253,11 @@ impl ClientCli {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn reply(&mut self, conversation_id: String, message: String) -> Result<()> {
|
pub async fn reply(&mut self, conversation_id: String, message: String) -> Result<()> {
|
||||||
self.send_message(OutgoingMessageTarget::Conversation(conversation_id), message)
|
self.send_message(
|
||||||
.await
|
OutgoingMessageTarget::Conversation(conversation_id),
|
||||||
|
message,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new_message(&mut self, handle_ids: Vec<String>, message: String) -> Result<()> {
|
pub async fn new_message(&mut self, handle_ids: Vec<String>, message: String) -> Result<()> {
|
||||||
|
|||||||
@@ -109,15 +109,20 @@ impl DaemonInterface for DBusDaemonInterface {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn enqueue_outgoing_message(
|
async fn reply(&mut self, conversation_id: String, text: String) -> Result<()> {
|
||||||
&mut self,
|
|
||||||
conversation_id: String,
|
|
||||||
text: String,
|
|
||||||
) -> Result<()> {
|
|
||||||
let attachment_guids: Vec<&str> = vec![];
|
let attachment_guids: Vec<&str> = vec![];
|
||||||
let outgoing_message_id = KordophoneRepository::send_message(
|
let outgoing_message_id =
|
||||||
|
KordophoneRepository::reply(&self.proxy(), &conversation_id, &text, attachment_guids)?;
|
||||||
|
println!("Outgoing message ID: {}", outgoing_message_id);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn new_conversation(&mut self, handle_ids: Vec<String>, text: String) -> Result<()> {
|
||||||
|
let attachment_guids: Vec<&str> = vec![];
|
||||||
|
let handle_ids: Vec<&str> = handle_ids.iter().map(String::as_str).collect();
|
||||||
|
let outgoing_message_id = KordophoneRepository::new_conversation(
|
||||||
&self.proxy(),
|
&self.proxy(),
|
||||||
&conversation_id,
|
handle_ids,
|
||||||
&text,
|
&text,
|
||||||
attachment_guids,
|
attachment_guids,
|
||||||
)?;
|
)?;
|
||||||
|
|||||||
@@ -21,11 +21,8 @@ pub trait DaemonInterface {
|
|||||||
conversation_id: String,
|
conversation_id: String,
|
||||||
last_message_id: Option<String>,
|
last_message_id: Option<String>,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
async fn enqueue_outgoing_message(
|
async fn reply(&mut self, conversation_id: String, text: String) -> Result<()>;
|
||||||
&mut self,
|
async fn new_conversation(&mut self, handle_ids: Vec<String>, text: String) -> Result<()>;
|
||||||
conversation_id: String,
|
|
||||||
text: String,
|
|
||||||
) -> Result<()>;
|
|
||||||
async fn wait_for_signals(&mut self) -> Result<()>;
|
async fn wait_for_signals(&mut self) -> Result<()>;
|
||||||
async fn config(&mut self, cmd: ConfigCommands) -> Result<()>;
|
async fn config(&mut self, cmd: ConfigCommands) -> Result<()>;
|
||||||
async fn delete_all_conversations(&mut self) -> Result<()>;
|
async fn delete_all_conversations(&mut self) -> Result<()>;
|
||||||
@@ -73,11 +70,12 @@ impl DaemonInterface for StubDaemonInterface {
|
|||||||
"Daemon interface not implemented on this platform"
|
"Daemon interface not implemented on this platform"
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
async fn enqueue_outgoing_message(
|
async fn reply(&mut self, _conversation_id: String, _text: String) -> Result<()> {
|
||||||
&mut self,
|
Err(anyhow::anyhow!(
|
||||||
_conversation_id: String,
|
"Daemon interface not implemented on this platform"
|
||||||
_text: String,
|
))
|
||||||
) -> Result<()> {
|
}
|
||||||
|
async fn new_conversation(&mut self, _handle_ids: Vec<String>, _text: String) -> Result<()> {
|
||||||
Err(anyhow::anyhow!(
|
Err(anyhow::anyhow!(
|
||||||
"Daemon interface not implemented on this platform"
|
"Daemon interface not implemented on this platform"
|
||||||
))
|
))
|
||||||
@@ -161,12 +159,20 @@ pub enum Commands {
|
|||||||
/// Deletes all conversations.
|
/// Deletes all conversations.
|
||||||
DeleteAllConversations,
|
DeleteAllConversations,
|
||||||
|
|
||||||
/// Enqueues an outgoing message to be sent to a conversation.
|
/// Replies to an existing conversation.
|
||||||
SendMessage {
|
#[command(alias = "send-message")]
|
||||||
|
Reply {
|
||||||
conversation_id: String,
|
conversation_id: String,
|
||||||
text: String,
|
text: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Starts a new conversation with one or more resolved handles.
|
||||||
|
New {
|
||||||
|
#[arg(long = "handle", required = true)]
|
||||||
|
handle_ids: Vec<String>,
|
||||||
|
text: String,
|
||||||
|
},
|
||||||
|
|
||||||
/// 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 { attachment_id: String },
|
DownloadAttachment { attachment_id: String },
|
||||||
|
|
||||||
@@ -208,10 +214,11 @@ impl Commands {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
Commands::DeleteAllConversations => client.delete_all_conversations().await,
|
Commands::DeleteAllConversations => client.delete_all_conversations().await,
|
||||||
Commands::SendMessage {
|
Commands::Reply {
|
||||||
conversation_id,
|
conversation_id,
|
||||||
text,
|
text,
|
||||||
} => client.enqueue_outgoing_message(conversation_id, text).await,
|
} => client.reply(conversation_id, text).await,
|
||||||
|
Commands::New { handle_ids, text } => client.new_conversation(handle_ids, text).await,
|
||||||
Commands::UploadAttachment { path } => client.upload_attachment(path).await,
|
Commands::UploadAttachment { path } => client.upload_attachment(path).await,
|
||||||
Commands::DownloadAttachment { attachment_id } => {
|
Commands::DownloadAttachment { attachment_id } => {
|
||||||
client.download_attachment(attachment_id).await
|
client.download_attachment(attachment_id).await
|
||||||
|
|||||||
@@ -371,11 +371,7 @@ impl DaemonInterface for XpcDaemonInterface {
|
|||||||
_ => Err(anyhow::anyhow!("Unexpected messages payload")),
|
_ => Err(anyhow::anyhow!("Unexpected messages payload")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn enqueue_outgoing_message(
|
async fn reply(&mut self, _conversation_id: String, _text: String) -> Result<()> {
|
||||||
&mut self,
|
|
||||||
_conversation_id: String,
|
|
||||||
_text: String,
|
|
||||||
) -> Result<()> {
|
|
||||||
let mach_port_name = Self::build_service_name()?;
|
let mach_port_name = Self::build_service_name()?;
|
||||||
let mut client = XPCClient::connect(&mach_port_name);
|
let mut client = XPCClient::connect(&mach_port_name);
|
||||||
let mut args = HashMap::new();
|
let mut args = HashMap::new();
|
||||||
@@ -387,10 +383,34 @@ impl DaemonInterface for XpcDaemonInterface {
|
|||||||
Self::key("text"),
|
Self::key("text"),
|
||||||
Message::String(CString::new(_text).unwrap()),
|
Message::String(CString::new(_text).unwrap()),
|
||||||
);
|
);
|
||||||
let reply = self
|
let response = self.call_method(&mut client, "Reply", Some(args)).await?;
|
||||||
.call_method(&mut client, "SendMessage", Some(args))
|
if let Some(uuid) = Self::get_string(&response, "uuid") {
|
||||||
|
println!("Outgoing message ID: {}", uuid.to_string_lossy());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn new_conversation(&mut self, handle_ids: Vec<String>, text: String) -> Result<()> {
|
||||||
|
let mach_port_name = Self::build_service_name()?;
|
||||||
|
let mut client = XPCClient::connect(&mach_port_name);
|
||||||
|
let mut args = HashMap::new();
|
||||||
|
args.insert(
|
||||||
|
Self::key("handle_ids"),
|
||||||
|
Message::Array(
|
||||||
|
handle_ids
|
||||||
|
.into_iter()
|
||||||
|
.map(|handle_id| Message::String(CString::new(handle_id).unwrap()))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
args.insert(
|
||||||
|
Self::key("text"),
|
||||||
|
Message::String(CString::new(text).unwrap()),
|
||||||
|
);
|
||||||
|
let response = self
|
||||||
|
.call_method(&mut client, "NewConversation", Some(args))
|
||||||
.await?;
|
.await?;
|
||||||
if let Some(uuid) = Self::get_string(&reply, "uuid") {
|
if let Some(uuid) = Self::get_string(&response, "uuid") {
|
||||||
println!("Outgoing message ID: {}", uuid.to_string_lossy());
|
println!("Outgoing message ID: {}", uuid.to_string_lossy());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -378,20 +378,22 @@ fn run_app(
|
|||||||
app.pinned_to_bottom = was_pinned;
|
app.pinned_to_bottom = was_pinned;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
daemon::Event::MessageSent {
|
daemon::Event::MessageQueued {
|
||||||
conversation_id,
|
conversation_id,
|
||||||
outgoing_id,
|
outgoing_id,
|
||||||
} => {
|
} => {
|
||||||
if app.active_conversation_id.as_deref() == Some(conversation_id.as_str()) {
|
if let Some(conversation_id) = conversation_id {
|
||||||
app.status = outgoing_id
|
if app.active_conversation_id.as_deref() == Some(conversation_id.as_str()) {
|
||||||
.as_deref()
|
app.status = outgoing_id
|
||||||
.map(|id| format!("Sent ({id})"))
|
.as_deref()
|
||||||
.unwrap_or_else(|| "Sent".to_string());
|
.map(|id| format!("Sent ({id})"))
|
||||||
app.refresh_messages_in_flight = false;
|
.unwrap_or_else(|| "Sent".to_string());
|
||||||
request_tx
|
app.refresh_messages_in_flight = false;
|
||||||
.send(daemon::Request::RefreshMessages { conversation_id })
|
request_tx
|
||||||
.ok();
|
.send(daemon::Request::RefreshMessages { conversation_id })
|
||||||
app.refresh_messages_in_flight = true;
|
.ok();
|
||||||
|
app.refresh_messages_in_flight = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
daemon::Event::MarkedRead => {}
|
daemon::Event::MarkedRead => {}
|
||||||
@@ -638,7 +640,7 @@ fn handle_chat_keys(
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
request_tx
|
request_tx
|
||||||
.send(daemon::Request::SendMessage {
|
.send(daemon::Request::Reply {
|
||||||
conversation_id,
|
conversation_id,
|
||||||
text,
|
text,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -50,8 +50,11 @@ namespace DBusService {
|
|||||||
[DBus (name = "GetMessages")]
|
[DBus (name = "GetMessages")]
|
||||||
public abstract GLib.HashTable<string, GLib.Variant>[] get_messages(string conversation_id, string last_message_id) throws DBusError, IOError;
|
public abstract GLib.HashTable<string, GLib.Variant>[] get_messages(string conversation_id, string last_message_id) throws DBusError, IOError;
|
||||||
|
|
||||||
[DBus (name = "SendMessage")]
|
[DBus (name = "Reply")]
|
||||||
public abstract string send_message(string conversation_id, string text, string[] attachment_guids) throws DBusError, IOError;
|
public abstract string reply(string conversation_id, string text, string[] attachment_guids) throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (name = "NewConversation")]
|
||||||
|
public abstract string new_conversation(string[] handle_ids, string text, string[] attachment_guids) throws DBusError, IOError;
|
||||||
|
|
||||||
[DBus (name = "MessagesUpdated")]
|
[DBus (name = "MessagesUpdated")]
|
||||||
public signal void messages_updated(string conversation_id);
|
public signal void messages_updated(string conversation_id);
|
||||||
|
|||||||
@@ -83,7 +83,7 @@
|
|||||||
</arg>
|
</arg>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
<method name="SendMessage">
|
<method name="Reply">
|
||||||
<arg type="s" name="conversation_id" direction="in"/>
|
<arg type="s" name="conversation_id" direction="in"/>
|
||||||
<arg type="s" name="text" direction="in"/>
|
<arg type="s" name="text" direction="in"/>
|
||||||
<arg type="as" name="attachment_guids" direction="in"/>
|
<arg type="as" name="attachment_guids" direction="in"/>
|
||||||
@@ -91,9 +91,28 @@
|
|||||||
<arg type="s" name="outgoing_message_id" direction="out"/>
|
<arg type="s" name="outgoing_message_id" direction="out"/>
|
||||||
|
|
||||||
<annotation name="org.freedesktop.DBus.DocString"
|
<annotation name="org.freedesktop.DBus.DocString"
|
||||||
value="Sends a message to the server. Returns the outgoing message ID.
|
value="Replies to an existing conversation. Returns the outgoing message ID.
|
||||||
Arguments:
|
Arguments:
|
||||||
- conversation_id: The ID of the conversation to send the message to.
|
- conversation_id: The ID of the conversation to reply to.
|
||||||
|
- text: The text of the message to send.
|
||||||
|
- attachment_guids: The GUIDs of the attachments to send.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- outgoing_message_id: The ID of the outgoing message.
|
||||||
|
"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="NewConversation">
|
||||||
|
<arg type="as" name="handle_ids" direction="in"/>
|
||||||
|
<arg type="s" name="text" direction="in"/>
|
||||||
|
<arg type="as" name="attachment_guids" direction="in"/>
|
||||||
|
|
||||||
|
<arg type="s" name="outgoing_message_id" direction="out"/>
|
||||||
|
|
||||||
|
<annotation name="org.freedesktop.DBus.DocString"
|
||||||
|
value="Sends a message to a new conversation identified by resolved handles.
|
||||||
|
Arguments:
|
||||||
|
- handle_ids: The resolved handles for the new conversation.
|
||||||
- text: The text of the message to send.
|
- text: The text of the message to send.
|
||||||
- attachment_guids: The GUIDs of the attachments to send.
|
- attachment_guids: The GUIDs of the attachments to send.
|
||||||
|
|
||||||
|
|||||||
@@ -96,12 +96,20 @@ public class Repository : DBusServiceProxy {
|
|||||||
return returned_messages;
|
return returned_messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string send_message(string conversation_guid, string message, string[] attachment_guids) throws DBusServiceProxyError, GLib.Error {
|
public string reply(string conversation_guid, string message, string[] attachment_guids) throws DBusServiceProxyError, GLib.Error {
|
||||||
if (dbus_repository == null) {
|
if (dbus_repository == null) {
|
||||||
throw new DBusServiceProxyError.NOT_CONNECTED("Repository not connected");
|
throw new DBusServiceProxyError.NOT_CONNECTED("Repository not connected");
|
||||||
}
|
}
|
||||||
|
|
||||||
return dbus_repository.send_message(conversation_guid, message, attachment_guids);
|
return dbus_repository.reply(conversation_guid, message, attachment_guids);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string new_conversation(string[] handle_ids, string message, string[] attachment_guids) throws DBusServiceProxyError, GLib.Error {
|
||||||
|
if (dbus_repository == null) {
|
||||||
|
throw new DBusServiceProxyError.NOT_CONNECTED("Repository not connected");
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbus_repository.new_conversation(handle_ids, message, attachment_guids);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sync_conversation(string conversation_guid) throws DBusServiceProxyError, GLib.Error {
|
public void sync_conversation(string conversation_guid) throws DBusServiceProxyError, GLib.Error {
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ class TranscriptContainerView : Adw.Bin
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Repository.get_instance().send_message(selected_conversation.guid, body, attachment_guids.to_array());
|
Repository.get_instance().reply(selected_conversation.guid, body, attachment_guids.to_array());
|
||||||
} catch (Error e) {
|
} catch (Error e) {
|
||||||
GLib.warning("Failed to send message: %s", e.message);
|
GLib.warning("Failed to send message: %s", e.message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ struct MessageEntryView: View
|
|||||||
|
|
||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
try await client.sendMessage(
|
try await client.reply(
|
||||||
conversationId: convo.id,
|
conversationId: convo.id,
|
||||||
message: messageText,
|
message: messageText,
|
||||||
transferGuids: transferGuids
|
transferGuids: transferGuids
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ final class XPCClient
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sendMessage(conversationId: String, message: String, transferGuids: Set<String>) async throws {
|
public func reply(conversationId: String, message: String, transferGuids: Set<String>) async throws {
|
||||||
var args: [String: xpc_object_t] = [:]
|
var args: [String: xpc_object_t] = [:]
|
||||||
args["conversation_id"] = xpcString(conversationId)
|
args["conversation_id"] = xpcString(conversationId)
|
||||||
args["text"] = xpcString(message)
|
args["text"] = xpcString(message)
|
||||||
@@ -142,7 +142,20 @@ final class XPCClient
|
|||||||
args["attachment_guids"] = xpcStringArray(transferGuids)
|
args["attachment_guids"] = xpcStringArray(transferGuids)
|
||||||
}
|
}
|
||||||
|
|
||||||
let req = makeRequest(method: "SendMessage", arguments: args)
|
let req = makeRequest(method: "Reply", arguments: args)
|
||||||
|
guard let reply = try await sendSync(req), xpc_get_type(reply) == XPC_TYPE_DICTIONARY else { throw Error.typeError }
|
||||||
|
}
|
||||||
|
|
||||||
|
public func newConversation(handleIds: Set<String>, message: String, transferGuids: Set<String>) async throws {
|
||||||
|
var args: [String: xpc_object_t] = [:]
|
||||||
|
args["handle_ids"] = xpcStringArray(handleIds)
|
||||||
|
args["text"] = xpcString(message)
|
||||||
|
|
||||||
|
if !transferGuids.isEmpty {
|
||||||
|
args["attachment_guids"] = xpcStringArray(transferGuids)
|
||||||
|
}
|
||||||
|
|
||||||
|
let req = makeRequest(method: "NewConversation", arguments: args)
|
||||||
guard let reply = try await sendSync(req), xpc_get_type(reply) == XPC_TYPE_DICTIONARY else { throw Error.typeError }
|
guard let reply = try await sendSync(req), xpc_get_type(reply) == XPC_TYPE_DICTIONARY else { throw Error.typeError }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,4 +424,3 @@ extension xpc_object_t
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user