[core] kordophoned-client: add support for settings and missing gui features
This commit is contained in:
3
core/Cargo.lock
generated
3
core/Cargo.lock
generated
@@ -1274,7 +1274,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kordophoned"
|
||||
version = "1.3.0"
|
||||
version = "1.3.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -1313,6 +1313,7 @@ dependencies = [
|
||||
"block",
|
||||
"dbus",
|
||||
"dbus-codegen",
|
||||
"keyring",
|
||||
"log",
|
||||
"xpc-connection",
|
||||
"xpc-connection-sys",
|
||||
|
||||
@@ -10,6 +10,7 @@ log = "0.4.22"
|
||||
# D-Bus dependencies only on Linux
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
dbus = "0.9.7"
|
||||
keyring = { version = "3.6.3", features = ["sync-secret-service"] }
|
||||
|
||||
# D-Bus codegen only on Linux
|
||||
[target.'cfg(target_os = "linux")'.build-dependencies]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
mod platform;
|
||||
mod worker;
|
||||
|
||||
pub use worker::{spawn_worker, ChatMessage, ConversationSummary, Event, Request};
|
||||
|
||||
pub use worker::{
|
||||
spawn_worker, Attachment, AttachmentInfo, AttachmentMetadata, AttributionInfo, ChatMessage,
|
||||
ConversationSummary, Event, Request, SettingsSnapshot,
|
||||
};
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#![cfg(target_os = "linux")]
|
||||
|
||||
use crate::worker::{ChatMessage, ConversationSummary, DaemonClient, Event};
|
||||
use crate::worker::{
|
||||
Attachment, AttachmentInfo, AttachmentMetadata, AttributionInfo, ChatMessage,
|
||||
ConversationSummary, DaemonClient, Event, SettingsSnapshot,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use dbus::arg::{PropMap, RefArg};
|
||||
use dbus::arg::{cast, PropMap, RefArg};
|
||||
use dbus::blocking::{Connection, Proxy};
|
||||
use dbus::channel::Token;
|
||||
use std::sync::mpsc::Sender;
|
||||
@@ -17,6 +20,7 @@ mod dbus_interface {
|
||||
include!(concat!(env!("OUT_DIR"), "/kordophone-client.rs"));
|
||||
}
|
||||
use dbus_interface::NetBuzzertKordophoneRepository as KordophoneRepository;
|
||||
use dbus_interface::NetBuzzertKordophoneSettings as KordophoneSettings;
|
||||
|
||||
pub(crate) struct DBusClient {
|
||||
conn: Connection,
|
||||
@@ -31,7 +35,7 @@ impl DBusClient {
|
||||
})
|
||||
}
|
||||
|
||||
fn proxy(&self) -> Proxy<&Connection> {
|
||||
fn proxy(&self) -> Proxy<'_, &Connection> {
|
||||
self.conn
|
||||
.with_proxy(DBUS_NAME, DBUS_PATH, std::time::Duration::from_millis(5000))
|
||||
}
|
||||
@@ -52,6 +56,10 @@ fn get_u32(map: &PropMap, key: &str) -> u32 {
|
||||
get_i64(map, key).try_into().unwrap_or(0)
|
||||
}
|
||||
|
||||
fn get_bool(map: &PropMap, key: &str) -> bool {
|
||||
map.get(key).and_then(|v| v.0.as_i64()).unwrap_or(0) != 0
|
||||
}
|
||||
|
||||
fn get_vec_string(map: &PropMap, key: &str) -> Vec<String> {
|
||||
map.get(key)
|
||||
.and_then(|v| v.0.as_iter())
|
||||
@@ -62,6 +70,122 @@ fn get_vec_string(map: &PropMap, key: &str) -> Vec<String> {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn clone_prop_map(map: &PropMap) -> PropMap {
|
||||
map.iter()
|
||||
.map(|(key, value)| (key.clone(), dbus::arg::Variant(value.0.box_clone())))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_prop_map(map: &PropMap, key: &str) -> Option<PropMap> {
|
||||
let value = map.get(key)?;
|
||||
|
||||
if let Some(prop_map) = cast::<PropMap>(&value.0) {
|
||||
return Some(clone_prop_map(prop_map));
|
||||
}
|
||||
|
||||
let mut iter = value.0.as_iter()?;
|
||||
let mut out = PropMap::new();
|
||||
loop {
|
||||
let Some(key) = iter.next().and_then(|arg| arg.as_str()) else {
|
||||
break;
|
||||
};
|
||||
let Some(value) = iter.next() else {
|
||||
break;
|
||||
};
|
||||
|
||||
out.insert(key.to_string(), dbus::arg::Variant(value.box_clone()));
|
||||
}
|
||||
|
||||
Some(out)
|
||||
}
|
||||
|
||||
fn get_vec_prop_map(map: &PropMap, key: &str) -> Vec<PropMap> {
|
||||
let Some(value) = map.get(key) else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
if let Some(items) = cast::<Vec<PropMap>>(&value.0) {
|
||||
return items.iter().map(clone_prop_map).collect();
|
||||
}
|
||||
|
||||
value
|
||||
.0
|
||||
.as_iter()
|
||||
.map(|iter| {
|
||||
iter.filter_map(|item| {
|
||||
let mut item_iter = item.as_iter()?;
|
||||
let mut out = PropMap::new();
|
||||
loop {
|
||||
let Some(key) = item_iter.next().and_then(|arg| arg.as_str()) else {
|
||||
break;
|
||||
};
|
||||
let Some(value) = item_iter.next() else {
|
||||
break;
|
||||
};
|
||||
|
||||
out.insert(key.to_string(), dbus::arg::Variant(value.box_clone()));
|
||||
}
|
||||
Some(out)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn attachment_from_prop_map(map: PropMap) -> Attachment {
|
||||
let metadata = get_prop_map(&map, "metadata").map(|metadata_map| {
|
||||
let attribution_info =
|
||||
get_prop_map(&metadata_map, "attribution_info").map(|info_map| AttributionInfo {
|
||||
width: info_map
|
||||
.get("width")
|
||||
.and_then(|v| v.0.as_i64())
|
||||
.and_then(|v| v.try_into().ok()),
|
||||
height: info_map
|
||||
.get("height")
|
||||
.and_then(|v| v.0.as_i64())
|
||||
.and_then(|v| v.try_into().ok()),
|
||||
});
|
||||
|
||||
AttachmentMetadata { attribution_info }
|
||||
});
|
||||
|
||||
Attachment {
|
||||
guid: get_string(&map, "guid"),
|
||||
downloaded: get_bool(&map, "downloaded"),
|
||||
preview_downloaded: get_bool(&map, "preview_downloaded"),
|
||||
metadata,
|
||||
}
|
||||
}
|
||||
|
||||
fn password_for_username(username: &str) -> Result<String> {
|
||||
if username.trim().is_empty() {
|
||||
return Ok(String::new());
|
||||
}
|
||||
|
||||
let entry = keyring::Entry::new("net.buzzert.kordophonecd", username)?;
|
||||
match entry.get_password() {
|
||||
Ok(password) => Ok(password),
|
||||
Err(keyring::Error::NoEntry) => Ok(String::new()),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_password_for_username(username: &str, password: &str) -> Result<()> {
|
||||
if username.trim().is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let entry = keyring::Entry::new("net.buzzert.kordophonecd", username)?;
|
||||
if password.is_empty() {
|
||||
match entry.delete_credential() {
|
||||
Ok(()) | Err(keyring::Error::NoEntry) => Ok(()),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
} else {
|
||||
entry.set_password(password).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl DaemonClient for DBusClient {
|
||||
fn get_conversations(&mut self, limit: i32, offset: i32) -> Result<Vec<ConversationSummary>> {
|
||||
let mut items = KordophoneRepository::get_conversations(&self.proxy(), limit, offset)?;
|
||||
@@ -83,6 +207,7 @@ impl DaemonClient for DBusClient {
|
||||
id,
|
||||
title,
|
||||
preview: get_string(&conv, "last_message_preview").replace('\n', " "),
|
||||
participants,
|
||||
unread_count: get_u32(&conv, "unread_count"),
|
||||
date_unix: get_i64(&conv, "date"),
|
||||
}
|
||||
@@ -107,20 +232,37 @@ impl DaemonClient for DBusClient {
|
||||
Ok(messages
|
||||
.into_iter()
|
||||
.map(|msg| ChatMessage {
|
||||
id: get_string(&msg, "id"),
|
||||
sender: get_string(&msg, "sender"),
|
||||
text: get_string(&msg, "text"),
|
||||
date_unix: get_i64(&msg, "date"),
|
||||
attachments: get_vec_prop_map(&msg, "attachments")
|
||||
.into_iter()
|
||||
.map(attachment_from_prop_map)
|
||||
.collect(),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn send_message(&mut self, conversation_id: String, text: String) -> Result<Option<String>> {
|
||||
let attachment_guids: Vec<&str> = vec![];
|
||||
self.send_message_with_attachments(conversation_id, text, Vec::new())
|
||||
}
|
||||
|
||||
fn send_message_with_attachments(
|
||||
&mut self,
|
||||
conversation_id: String,
|
||||
text: String,
|
||||
attachment_guids: Vec<String>,
|
||||
) -> Result<Option<String>> {
|
||||
let attachment_guid_refs = attachment_guids
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.collect::<Vec<_>>();
|
||||
let outgoing_id = KordophoneRepository::send_message(
|
||||
&self.proxy(),
|
||||
&conversation_id,
|
||||
&text,
|
||||
attachment_guids,
|
||||
attachment_guid_refs,
|
||||
)?;
|
||||
Ok(Some(outgoing_id))
|
||||
}
|
||||
@@ -135,6 +277,61 @@ impl DaemonClient for DBusClient {
|
||||
.map_err(|e| anyhow::anyhow!("Failed to sync conversation: {e}"))
|
||||
}
|
||||
|
||||
fn sync_conversation_list(&mut self) -> Result<()> {
|
||||
KordophoneRepository::sync_conversation_list(&self.proxy())
|
||||
.map_err(|e| anyhow::anyhow!("Failed to sync conversation list: {e}"))
|
||||
}
|
||||
|
||||
fn sync_all_conversations(&mut self) -> Result<()> {
|
||||
KordophoneRepository::sync_all_conversations(&self.proxy())
|
||||
.map_err(|e| anyhow::anyhow!("Failed to sync all conversations: {e}"))
|
||||
}
|
||||
|
||||
fn download_attachment(&mut self, attachment_guid: String, preview: bool) -> Result<()> {
|
||||
KordophoneRepository::download_attachment(&self.proxy(), &attachment_guid, preview)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to download attachment: {e}"))
|
||||
}
|
||||
|
||||
fn get_attachment_info(&mut self, attachment_guid: String) -> Result<AttachmentInfo> {
|
||||
let (path, preview_path, downloaded, preview_downloaded) =
|
||||
KordophoneRepository::get_attachment_info(&self.proxy(), &attachment_guid)?;
|
||||
|
||||
Ok(AttachmentInfo {
|
||||
path,
|
||||
preview_path,
|
||||
downloaded,
|
||||
preview_downloaded,
|
||||
})
|
||||
}
|
||||
|
||||
fn upload_attachment(&mut self, path: String) -> Result<String> {
|
||||
KordophoneRepository::upload_attachment(&self.proxy(), &path)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to upload attachment: {e}"))
|
||||
}
|
||||
|
||||
fn get_settings(&mut self) -> Result<SettingsSnapshot> {
|
||||
let server_url = KordophoneSettings::server_url(&self.proxy()).unwrap_or_default();
|
||||
let username = KordophoneSettings::username(&self.proxy()).unwrap_or_default();
|
||||
let password = password_for_username(&username)?;
|
||||
|
||||
Ok(SettingsSnapshot {
|
||||
server_url,
|
||||
username,
|
||||
password,
|
||||
})
|
||||
}
|
||||
|
||||
fn save_settings(
|
||||
&mut self,
|
||||
server_url: String,
|
||||
username: String,
|
||||
password: String,
|
||||
) -> Result<()> {
|
||||
KordophoneSettings::set_server(&self.proxy(), &server_url, &username)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to save daemon settings: {e}"))?;
|
||||
set_password_for_username(&username, &password)
|
||||
}
|
||||
|
||||
fn install_signal_handlers(&mut self, event_tx: Sender<Event>) -> Result<()> {
|
||||
let conversations_tx = event_tx.clone();
|
||||
let t1 = self
|
||||
@@ -164,7 +361,7 @@ impl DaemonClient for DBusClient {
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to match MessagesUpdated: {e}"))?;
|
||||
|
||||
let reconnected_tx = event_tx;
|
||||
let reconnected_tx = event_tx.clone();
|
||||
let t3 = self
|
||||
.proxy()
|
||||
.match_signal(
|
||||
@@ -177,7 +374,54 @@ impl DaemonClient for DBusClient {
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to match UpdateStreamReconnected: {e}"))?;
|
||||
|
||||
self.signal_tokens.extend([t1, t2, t3]);
|
||||
let download_tx = event_tx.clone();
|
||||
let t4 = self
|
||||
.proxy()
|
||||
.match_signal(
|
||||
move |s: dbus_interface::NetBuzzertKordophoneRepositoryAttachmentDownloadCompleted,
|
||||
_: &Connection,
|
||||
_: &dbus::message::Message| {
|
||||
let _ = download_tx.send(Event::AttachmentDownloaded {
|
||||
attachment_guid: s.attachment_id,
|
||||
});
|
||||
true
|
||||
},
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to match AttachmentDownloadCompleted: {e}"))?;
|
||||
|
||||
let failed_tx = event_tx.clone();
|
||||
let t5 = self
|
||||
.proxy()
|
||||
.match_signal(
|
||||
move |s: dbus_interface::NetBuzzertKordophoneRepositoryAttachmentDownloadFailed,
|
||||
_: &Connection,
|
||||
_: &dbus::message::Message| {
|
||||
let _ = failed_tx.send(Event::AttachmentDownloadFailed {
|
||||
attachment_guid: s.attachment_id,
|
||||
error: s.error_message,
|
||||
});
|
||||
true
|
||||
},
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to match AttachmentDownloadFailed: {e}"))?;
|
||||
|
||||
let upload_tx = event_tx;
|
||||
let t6 = self
|
||||
.proxy()
|
||||
.match_signal(
|
||||
move |s: dbus_interface::NetBuzzertKordophoneRepositoryAttachmentUploadCompleted,
|
||||
_: &Connection,
|
||||
_: &dbus::message::Message| {
|
||||
let _ = upload_tx.send(Event::AttachmentUploaded {
|
||||
upload_guid: s.upload_guid,
|
||||
attachment_guid: s.attachment_guid,
|
||||
});
|
||||
true
|
||||
},
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to match AttachmentUploadCompleted: {e}"))?;
|
||||
|
||||
self.signal_tokens.extend([t1, t2, t3, t4, t5, t6]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -186,4 +430,3 @@ impl DaemonClient for DBusClient {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,6 +142,7 @@ impl DaemonClient for XpcClient {
|
||||
id,
|
||||
title,
|
||||
preview: preview.replace('\n', " "),
|
||||
participants,
|
||||
unread_count,
|
||||
date_unix,
|
||||
});
|
||||
@@ -180,9 +181,11 @@ impl DaemonClient for XpcClient {
|
||||
for item in items {
|
||||
let Message::Dictionary(msg) = item else { continue };
|
||||
messages.push(ChatMessage {
|
||||
id: Self::get_string(msg, "id").unwrap_or_default(),
|
||||
sender: Self::get_string(msg, "sender").unwrap_or_default(),
|
||||
text: Self::get_string(msg, "text").unwrap_or_default(),
|
||||
date_unix: Self::get_i64_from_str(msg, "date"),
|
||||
attachments: Vec::new(),
|
||||
});
|
||||
}
|
||||
Ok(messages)
|
||||
@@ -230,4 +233,3 @@ impl DaemonClient for XpcClient {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,23 +8,98 @@ pub struct ConversationSummary {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
pub preview: String,
|
||||
pub participants: Vec<String>,
|
||||
pub unread_count: u32,
|
||||
pub date_unix: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ChatMessage {
|
||||
pub id: String,
|
||||
pub sender: String,
|
||||
pub text: String,
|
||||
pub date_unix: i64,
|
||||
pub attachments: Vec<Attachment>,
|
||||
}
|
||||
|
||||
impl ChatMessage {
|
||||
pub fn from_me(&self) -> bool {
|
||||
self.sender == "(Me)"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct AttributionInfo {
|
||||
pub width: Option<u32>,
|
||||
pub height: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct AttachmentMetadata {
|
||||
pub attribution_info: Option<AttributionInfo>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Attachment {
|
||||
pub guid: String,
|
||||
pub downloaded: bool,
|
||||
pub preview_downloaded: bool,
|
||||
pub metadata: Option<AttachmentMetadata>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct AttachmentInfo {
|
||||
pub path: String,
|
||||
pub preview_path: String,
|
||||
pub downloaded: bool,
|
||||
pub preview_downloaded: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SettingsSnapshot {
|
||||
pub server_url: String,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
pub enum Request {
|
||||
RefreshConversations,
|
||||
RefreshMessages { conversation_id: String },
|
||||
SendMessage { conversation_id: String, text: String },
|
||||
MarkRead { conversation_id: String },
|
||||
SyncConversation { conversation_id: String },
|
||||
RefreshMessages {
|
||||
conversation_id: String,
|
||||
},
|
||||
SendMessage {
|
||||
conversation_id: String,
|
||||
text: String,
|
||||
},
|
||||
SendMessageWithAttachments {
|
||||
conversation_id: String,
|
||||
text: String,
|
||||
attachment_guids: Vec<String>,
|
||||
},
|
||||
MarkRead {
|
||||
conversation_id: String,
|
||||
},
|
||||
SyncConversation {
|
||||
conversation_id: String,
|
||||
},
|
||||
SyncConversationList,
|
||||
SyncAllConversations,
|
||||
DownloadAttachment {
|
||||
attachment_guid: String,
|
||||
preview: bool,
|
||||
},
|
||||
GetAttachmentInfo {
|
||||
attachment_guid: String,
|
||||
},
|
||||
UploadAttachment {
|
||||
path: String,
|
||||
},
|
||||
LoadSettings,
|
||||
SaveSettings {
|
||||
server_url: String,
|
||||
username: String,
|
||||
password: String,
|
||||
},
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
@@ -38,9 +113,40 @@ pub enum Event {
|
||||
outgoing_id: Option<String>,
|
||||
},
|
||||
MarkedRead,
|
||||
ConversationSyncTriggered { conversation_id: String },
|
||||
ConversationSyncTriggered {
|
||||
conversation_id: String,
|
||||
},
|
||||
ConversationListSyncTriggered,
|
||||
AllConversationsSyncTriggered,
|
||||
AttachmentDownloadQueued {
|
||||
attachment_guid: String,
|
||||
preview: bool,
|
||||
},
|
||||
AttachmentDownloaded {
|
||||
attachment_guid: String,
|
||||
},
|
||||
AttachmentDownloadFailed {
|
||||
attachment_guid: String,
|
||||
error: String,
|
||||
},
|
||||
AttachmentUploaded {
|
||||
upload_guid: String,
|
||||
attachment_guid: String,
|
||||
},
|
||||
AttachmentUploadQueued {
|
||||
upload_guid: String,
|
||||
path: String,
|
||||
},
|
||||
AttachmentInfo {
|
||||
attachment_guid: String,
|
||||
info: AttachmentInfo,
|
||||
},
|
||||
SettingsLoaded(SettingsSnapshot),
|
||||
SettingsSaved,
|
||||
ConversationsUpdated,
|
||||
MessagesUpdated { conversation_id: String },
|
||||
MessagesUpdated {
|
||||
conversation_id: String,
|
||||
},
|
||||
UpdateStreamReconnected,
|
||||
Error(String),
|
||||
}
|
||||
@@ -59,16 +165,18 @@ pub fn spawn_worker(
|
||||
};
|
||||
|
||||
if let Err(e) = client.install_signal_handlers(event_tx.clone()) {
|
||||
let _ = event_tx.send(Event::Error(format!("Failed to install daemon signals: {e}")));
|
||||
let _ = event_tx.send(Event::Error(format!(
|
||||
"Failed to install daemon signals: {e}"
|
||||
)));
|
||||
}
|
||||
|
||||
loop {
|
||||
match request_rx.recv_timeout(Duration::from_millis(100)) {
|
||||
Ok(req) => {
|
||||
let res = match req {
|
||||
Request::RefreshConversations => client
|
||||
.get_conversations(200, 0)
|
||||
.map(Event::Conversations),
|
||||
Request::RefreshConversations => {
|
||||
client.get_conversations(200, 0).map(Event::Conversations)
|
||||
}
|
||||
Request::RefreshMessages { conversation_id } => client
|
||||
.get_messages(conversation_id.clone(), None)
|
||||
.map(|messages| Event::Messages {
|
||||
@@ -78,8 +186,24 @@ pub fn spawn_worker(
|
||||
Request::SendMessage {
|
||||
conversation_id,
|
||||
text,
|
||||
} => client
|
||||
} => {
|
||||
client
|
||||
.send_message(conversation_id.clone(), text)
|
||||
.map(|outgoing_id| Event::MessageSent {
|
||||
conversation_id,
|
||||
outgoing_id,
|
||||
})
|
||||
}
|
||||
Request::SendMessageWithAttachments {
|
||||
conversation_id,
|
||||
text,
|
||||
attachment_guids,
|
||||
} => client
|
||||
.send_message_with_attachments(
|
||||
conversation_id.clone(),
|
||||
text,
|
||||
attachment_guids,
|
||||
)
|
||||
.map(|outgoing_id| Event::MessageSent {
|
||||
conversation_id,
|
||||
outgoing_id,
|
||||
@@ -90,6 +214,38 @@ pub fn spawn_worker(
|
||||
Request::SyncConversation { conversation_id } => client
|
||||
.sync_conversation(conversation_id.clone())
|
||||
.map(|_| Event::ConversationSyncTriggered { conversation_id }),
|
||||
Request::SyncConversationList => client
|
||||
.sync_conversation_list()
|
||||
.map(|_| Event::ConversationListSyncTriggered),
|
||||
Request::SyncAllConversations => client
|
||||
.sync_all_conversations()
|
||||
.map(|_| Event::AllConversationsSyncTriggered),
|
||||
Request::DownloadAttachment {
|
||||
attachment_guid,
|
||||
preview,
|
||||
} => client
|
||||
.download_attachment(attachment_guid.clone(), preview)
|
||||
.map(|_| Event::AttachmentDownloadQueued {
|
||||
attachment_guid,
|
||||
preview,
|
||||
}),
|
||||
Request::GetAttachmentInfo { attachment_guid } => client
|
||||
.get_attachment_info(attachment_guid.clone())
|
||||
.map(|info| Event::AttachmentInfo {
|
||||
attachment_guid,
|
||||
info,
|
||||
}),
|
||||
Request::UploadAttachment { path } => client
|
||||
.upload_attachment(path.clone())
|
||||
.map(|upload_guid| Event::AttachmentUploadQueued { upload_guid, path }),
|
||||
Request::LoadSettings => client.get_settings().map(Event::SettingsLoaded),
|
||||
Request::SaveSettings {
|
||||
server_url,
|
||||
username,
|
||||
password,
|
||||
} => client
|
||||
.save_settings(server_url, username, password)
|
||||
.map(|_| Event::SettingsSaved),
|
||||
};
|
||||
|
||||
match res {
|
||||
@@ -120,8 +276,43 @@ pub(crate) trait DaemonClient {
|
||||
last_message_id: Option<String>,
|
||||
) -> Result<Vec<ChatMessage>>;
|
||||
fn send_message(&mut self, conversation_id: String, text: String) -> Result<Option<String>>;
|
||||
fn send_message_with_attachments(
|
||||
&mut self,
|
||||
conversation_id: String,
|
||||
text: String,
|
||||
attachment_guids: Vec<String>,
|
||||
) -> Result<Option<String>> {
|
||||
let _ = attachment_guids;
|
||||
self.send_message(conversation_id, text)
|
||||
}
|
||||
fn mark_conversation_as_read(&mut self, conversation_id: String) -> Result<()>;
|
||||
fn sync_conversation(&mut self, conversation_id: String) -> Result<()>;
|
||||
fn sync_conversation_list(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn sync_all_conversations(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn download_attachment(&mut self, _attachment_guid: String, _preview: bool) -> Result<()> {
|
||||
anyhow::bail!("Attachment downloads are not supported on this platform")
|
||||
}
|
||||
fn get_attachment_info(&mut self, _attachment_guid: String) -> Result<AttachmentInfo> {
|
||||
anyhow::bail!("Attachment info is not supported on this platform")
|
||||
}
|
||||
fn upload_attachment(&mut self, _path: String) -> Result<String> {
|
||||
anyhow::bail!("Attachment uploads are not supported on this platform")
|
||||
}
|
||||
fn get_settings(&mut self) -> Result<SettingsSnapshot> {
|
||||
anyhow::bail!("Settings are not supported on this platform")
|
||||
}
|
||||
fn save_settings(
|
||||
&mut self,
|
||||
_server_url: String,
|
||||
_username: String,
|
||||
_password: String,
|
||||
) -> Result<()> {
|
||||
anyhow::bail!("Settings are not supported on this platform")
|
||||
}
|
||||
fn install_signal_handlers(&mut self, _event_tx: mpsc::Sender<Event>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -130,4 +321,3 @@ pub(crate) trait DaemonClient {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user