Private
Public Access
1
0

implements settings, conversation dbus encoding

This commit is contained in:
2025-04-27 18:07:58 -07:00
parent 49f8b81b9c
commit cecfd7cd76
11 changed files with 446 additions and 96 deletions

View File

@@ -13,7 +13,10 @@
'id' (string): Unique identifier
'title' (string): Display name
'last_message' (string): Preview text
'is_unread' (boolean): Unread status"/>
'is_unread' (boolean): Unread status
'date' (int64): Date of last message
'participants' (array of strings): List of participants
'unread_count' (int32): Number of unread messages"/>
</arg>
</method>

View File

@@ -1,5 +1,6 @@
use tokio::sync::oneshot;
use kordophone_db::models::Conversation;
use crate::daemon::settings::Settings;
pub type Reply<T> = oneshot::Sender<T>;
@@ -13,6 +14,12 @@ pub enum Event {
/// Returns all known conversations from the database.
GetAllConversations(Reply<Vec<Conversation>>),
/// Returns all known settings from the database.
GetAllSettings(Reply<Settings>),
/// Update settings in the database.
UpdateSettings(Settings, Reply<()>),
}

View File

@@ -50,6 +50,10 @@ impl TokenStore for DatabaseTokenStore {
}
}
mod target {
pub static SYNC: &str = "sync";
}
pub struct Daemon {
pub event_sender: Sender<Event>,
event_receiver: Receiver<Event>,
@@ -110,6 +114,25 @@ impl Daemon {
let conversations = self.get_conversations().await;
reply.send(conversations).unwrap();
},
Event::GetAllSettings(reply) => {
let settings = self.get_settings().await
.unwrap_or_else(|e| {
log::error!("Failed to get settings: {:#?}", e);
Settings::default()
});
reply.send(settings).unwrap();
},
Event::UpdateSettings(settings, reply) => {
self.update_settings(settings).await
.unwrap_or_else(|e| {
log::error!("Failed to update settings: {}", e);
});
reply.send(()).unwrap();
},
}
}
@@ -118,32 +141,9 @@ impl Daemon {
}
async fn sync_all_conversations_impl(mut database: Arc<Mutex<Database>>) -> Result<()> {
log::info!("Starting conversation sync");
// Get client from the database
let settings = database.with_settings(|s| Settings::from_db(s))
.await?;
log::info!(target: target::SYNC, "Starting conversation sync");
let server_url = settings.server_url
.ok_or(DaemonError::ClientNotConfigured)?;
let mut client = HTTPAPIClient::new(
server_url.parse().unwrap(),
match (settings.username, settings.credential_item) {
(Some(username), Some(password)) => Some(
Credentials {
username,
password,
}
),
_ => None,
},
DatabaseTokenStore { database: database.clone() }
);
// This function needed to implement TokenManagement
// let token = database.lock().await.get_token();
// TODO: Clent.token = token
let mut client = Self::get_client_impl(database.clone()).await?;
// Fetch conversations from server
let fetched_conversations = client.get_conversations().await?;
@@ -152,6 +152,7 @@ impl Daemon {
.collect();
// Process each conversation
let num_conversations = db_conversations.len();
for conversation in db_conversations {
let conversation_id = conversation.guid.clone();
@@ -159,21 +160,18 @@ impl Daemon {
database.with_repository(|r| r.insert_conversation(conversation)).await?;
// Fetch and sync messages for this conversation
log::info!(target: target::SYNC, "Fetching messages for conversation {}", conversation_id);
let messages = client.get_messages(&conversation_id).await?;
let db_messages: Vec<kordophone_db::models::Message> = messages.into_iter()
.map(|m| kordophone_db::models::Message::from(m))
.collect();
// Insert each message
database.with_repository(|r| -> Result<()> {
for message in db_messages {
r.insert_message(&conversation_id, message)?;
}
Ok(())
}).await?;
log::info!(target: target::SYNC, "Inserting {} messages for conversation {}", db_messages.len(), conversation_id);
database.with_repository(|r| r.insert_messages(&conversation_id, db_messages)).await?;
}
log::info!(target: target::SYNC, "Synchronized {} conversations", num_conversations);
Ok(())
}
@@ -185,8 +183,16 @@ impl Daemon {
Ok(settings)
}
async fn update_settings(&mut self, settings: Settings) -> Result<()> {
self.database.with_settings(|s| settings.save(s)).await
}
async fn get_client(&mut self) -> Result<HTTPAPIClient<DatabaseTokenStore>> {
let settings = self.database.with_settings(|s|
Self::get_client_impl(self.database.clone()).await
}
async fn get_client_impl(mut database: Arc<Mutex<Database>>) -> Result<HTTPAPIClient<DatabaseTokenStore>> {
let settings = database.with_settings(|s|
Settings::from_db(s)
).await?;
@@ -205,7 +211,7 @@ impl Daemon {
),
_ => None,
},
DatabaseTokenStore { database: self.database.clone() }
DatabaseTokenStore { database: database.clone() }
);
Ok(client)

View File

@@ -16,9 +16,9 @@ pub struct Settings {
impl Settings {
pub fn from_db(db_settings: &mut DbSettings) -> Result<Self> {
let server_url = db_settings.get(keys::SERVER_URL)?;
let username = db_settings.get(keys::USERNAME)?;
let credential_item = db_settings.get(keys::CREDENTIAL_ITEM)?;
let server_url: Option<String> = db_settings.get(keys::SERVER_URL)?;
let username: Option<String> = db_settings.get(keys::USERNAME)?;
let credential_item: Option<String> = db_settings.get(keys::CREDENTIAL_ITEM)?;
Ok(Self {
server_url,
@@ -28,9 +28,25 @@ impl Settings {
}
pub fn save(&self, db_settings: &mut DbSettings) -> Result<()> {
db_settings.put(keys::SERVER_URL, &self.server_url)?;
db_settings.put(keys::USERNAME, &self.username)?;
db_settings.put(keys::CREDENTIAL_ITEM, &self.credential_item)?;
if let Some(server_url) = &self.server_url {
db_settings.put(keys::SERVER_URL, &server_url)?;
}
if let Some(username) = &self.username {
db_settings.put(keys::USERNAME, &username)?;
}
if let Some(credential_item) = &self.credential_item {
db_settings.put(keys::CREDENTIAL_ITEM, &credential_item)?;
}
Ok(())
}
}
impl Default for Settings {
fn default() -> Self {
Self {
server_url: None,
username: None,
credential_item: None,
}
}
}

View File

@@ -1,17 +1,14 @@
use dbus::arg;
use dbus_tree::MethodErr;
use std::sync::Arc;
use tokio::sync::{Mutex, MutexGuard};
use tokio::sync::mpsc;
use std::future::Future;
use std::thread;
use tokio::sync::oneshot;
use tokio::sync::mpsc;
use futures_util::future::FutureExt;
use crate::daemon::{
Daemon,
DaemonResult,
events::{Event, Reply},
settings::Settings,
};
use crate::dbus::interface::NetBuzzertKordophoneRepository as DbusRepository;
@@ -63,7 +60,10 @@ impl DbusRepository for ServerImpl {
map.insert("guid".into(), arg::Variant(Box::new(conv.guid)));
map.insert("display_name".into(), arg::Variant(Box::new(conv.display_name.unwrap_or_default())));
map.insert("unread_count".into(), arg::Variant(Box::new(conv.unread_count as i32)));
map
map.insert("last_message_preview".into(), arg::Variant(Box::new(conv.last_message_preview.unwrap_or_default())));
map.insert("participants".into(), arg::Variant(Box::new(conv.participants.into_iter().map(|p| p.display_name()).collect::<Vec<String>>())));
map.insert("date".into(), arg::Variant(Box::new(conv.date.and_utc().timestamp())));
map
}).collect();
Ok(result)
@@ -77,35 +77,77 @@ impl DbusRepository for ServerImpl {
impl DbusSettings for ServerImpl {
fn set_server(&mut self, url: String, user: String) -> Result<(), dbus::MethodErr> {
todo!()
self.send_event_sync(|r|
Event::UpdateSettings(Settings {
server_url: Some(url),
username: Some(user),
credential_item: None,
}, r)
)
}
fn set_credential_item_(&mut self, item_path: dbus::Path<'static>) -> Result<(), dbus::MethodErr> {
todo!()
self.send_event_sync(|r|
Event::UpdateSettings(Settings {
server_url: None,
username: None,
credential_item: Some(item_path.to_string()),
}, r)
)
}
fn server_url(&self) -> Result<String, dbus::MethodErr> {
todo!()
self.send_event_sync(Event::GetAllSettings)
.and_then(|settings| {
Ok(settings.server_url.unwrap_or_default())
})
}
fn set_server_url(&self, value: String) -> Result<(), dbus::MethodErr> {
todo!()
self.send_event_sync(|r|
Event::UpdateSettings(Settings {
server_url: Some(value),
username: None,
credential_item: None,
}, r)
)
}
fn username(&self) -> Result<String, dbus::MethodErr> {
todo!()
self.send_event_sync(Event::GetAllSettings)
.and_then(|settings| {
Ok(settings.username.unwrap_or_default())
})
}
fn set_username(&self, value: String) -> Result<(), dbus::MethodErr> {
todo!()
self.send_event_sync(|r|
Event::UpdateSettings(Settings {
server_url: None,
username: Some(value),
credential_item: None,
}, r)
)
}
fn credential_item(&self) -> Result<dbus::Path<'static>, dbus::MethodErr> {
todo!()
self.send_event_sync(Event::GetAllSettings)
.and_then(|settings| {
Ok(settings.credential_item.unwrap_or_default())
})
.and_then(|item| {
Ok(dbus::Path::new(item).unwrap_or_default())
})
}
fn set_credential_item(&self, value: dbus::Path<'static>) -> Result<(), dbus::MethodErr> {
todo!()
self.send_event_sync(|r|
Event::UpdateSettings(Settings {
server_url: None,
username: None,
credential_item: Some(value.to_string()),
}, r)
)
}
}