199 lines
6.4 KiB
Rust
199 lines
6.4 KiB
Rust
use crate::storage_error::StorageError;
|
|
use crate::users::contact::Contact;
|
|
use crate::util::db;
|
|
use crate::util::sync::{self, EntityType, Operation};
|
|
use rusqlite::{OptionalExtension, params};
|
|
|
|
pub(crate) fn upsert_contact(
|
|
tx: &rusqlite::Transaction<'_>,
|
|
storage_owner: i64,
|
|
contact: &Contact,
|
|
) -> Result<(), StorageError> {
|
|
tx.execute(
|
|
r#"
|
|
INSERT INTO contacts (
|
|
storage_owner, user_id, user_name, created_at, last_message_at,
|
|
notifications, notifications_read_at
|
|
)
|
|
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
|
|
ON CONFLICT(storage_owner, user_id) DO UPDATE SET
|
|
user_name = COALESCE(excluded.user_name, contacts.user_name),
|
|
created_at = MIN(contacts.created_at, excluded.created_at),
|
|
last_message_at = CASE
|
|
WHEN excluded.last_message_at IS NULL THEN contacts.last_message_at
|
|
WHEN contacts.last_message_at IS NULL THEN excluded.last_message_at
|
|
ELSE MAX(contacts.last_message_at, excluded.last_message_at)
|
|
END
|
|
"#,
|
|
params![
|
|
storage_owner,
|
|
contact.user_id,
|
|
contact.user_name,
|
|
contact.created_at,
|
|
contact.last_message_at,
|
|
contact.notifications,
|
|
contact.notifications_read_at,
|
|
],
|
|
)?;
|
|
sync::record_event(
|
|
tx,
|
|
storage_owner,
|
|
EntityType::Contact,
|
|
contact.user_id,
|
|
Operation::Upsert,
|
|
)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn has_user(storage_owner: i64, user_id: i64) -> Result<bool, StorageError> {
|
|
db::with_db(|conn| {
|
|
Ok(conn.query_row(
|
|
"SELECT EXISTS(SELECT 1 FROM contacts WHERE storage_owner = ?1 AND user_id = ?2)",
|
|
params![storage_owner, user_id],
|
|
|row| row.get(0),
|
|
)?)
|
|
})
|
|
}
|
|
|
|
pub fn mod_user(storage_owner: i64, contact: &Contact) -> Result<(), StorageError> {
|
|
db::with_immediate_transaction(|tx| upsert_contact(tx, storage_owner, contact))
|
|
}
|
|
|
|
pub fn get_users_by_ids(storage_owner: i64, ids: &[i64]) -> Result<Vec<Contact>, StorageError> {
|
|
if ids.is_empty() {
|
|
return Ok(Vec::new());
|
|
}
|
|
let wanted: std::collections::HashSet<i64> = ids.iter().copied().collect();
|
|
Ok(get_users(storage_owner)?
|
|
.into_iter()
|
|
.filter(|contact| wanted.contains(&contact.user_id))
|
|
.collect())
|
|
}
|
|
|
|
pub fn get_user(storage_owner: i64, user_id: i64) -> Result<Option<Contact>, StorageError> {
|
|
db::with_db(|conn| {
|
|
match conn.query_row(
|
|
r#"
|
|
SELECT user_id, user_name, created_at, last_message_at,
|
|
notifications, notifications_read_at
|
|
FROM contacts
|
|
WHERE storage_owner = ?1 AND user_id = ?2
|
|
LIMIT 1
|
|
"#,
|
|
params![storage_owner, user_id],
|
|
|r| {
|
|
Ok(Contact {
|
|
user_id: r.get(0)?,
|
|
user_name: r.get(1)?,
|
|
created_at: r.get(2)?,
|
|
last_message_at: r.get(3)?,
|
|
notifications: r.get(4)?,
|
|
notifications_read_at: r.get(5)?,
|
|
})
|
|
},
|
|
) {
|
|
Ok(c) => Ok(Some(c)),
|
|
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
|
|
Err(e) => Err(e.into()),
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn get_users(storage_owner: i64) -> Result<Vec<Contact>, StorageError> {
|
|
db::with_db(|conn| {
|
|
let mut stmt = conn.prepare(
|
|
r#"
|
|
SELECT user_id, user_name, created_at, last_message_at,
|
|
notifications, notifications_read_at
|
|
FROM contacts
|
|
WHERE storage_owner = ?1
|
|
ORDER BY
|
|
CASE WHEN last_message_at IS NULL THEN 1 ELSE 0 END,
|
|
last_message_at DESC,
|
|
user_id ASC
|
|
"#,
|
|
)?;
|
|
|
|
let rows = stmt.query_map(params![storage_owner], |r| {
|
|
Ok(Contact {
|
|
user_id: r.get(0)?,
|
|
user_name: r.get(1)?,
|
|
created_at: r.get(2)?,
|
|
last_message_at: r.get(3)?,
|
|
notifications: r.get(4)?,
|
|
notifications_read_at: r.get(5)?,
|
|
})
|
|
})?;
|
|
|
|
let mut out = Vec::new();
|
|
for row in rows {
|
|
out.push(row?);
|
|
}
|
|
Ok(out)
|
|
})
|
|
}
|
|
|
|
pub fn read_notifications(
|
|
storage_owner: i64,
|
|
user_id: i64,
|
|
through: i64,
|
|
) -> Result<Option<Contact>, StorageError> {
|
|
db::with_immediate_transaction(|tx| {
|
|
let Some(current_read_at) = tx
|
|
.query_row(
|
|
"SELECT notifications_read_at FROM contacts WHERE storage_owner = ?1 AND user_id = ?2",
|
|
params![storage_owner, user_id],
|
|
|row| row.get::<_, i64>(0),
|
|
)
|
|
.optional()?
|
|
else {
|
|
return Ok(None);
|
|
};
|
|
let read_at = current_read_at.max(through);
|
|
let notifications = tx.query_row(
|
|
r#"
|
|
SELECT COUNT(*)
|
|
FROM messages
|
|
WHERE storage_owner = ?1
|
|
AND external_user = ?2
|
|
AND sent_by_self = 0
|
|
AND deleted_by_external = 0
|
|
AND history_deleted = 0
|
|
AND COALESCE(destination_iota_received_at, stored_at, authored_at, message_time) > ?3
|
|
"#,
|
|
params![storage_owner, user_id, read_at],
|
|
|row| row.get::<_, i64>(0),
|
|
)?;
|
|
tx.execute(
|
|
"UPDATE contacts SET notifications = ?3, notifications_read_at = ?4 WHERE storage_owner = ?1 AND user_id = ?2",
|
|
params![storage_owner, user_id, notifications, read_at],
|
|
)?;
|
|
sync::record_event(
|
|
tx,
|
|
storage_owner,
|
|
EntityType::Contact,
|
|
user_id,
|
|
Operation::Upsert,
|
|
)?;
|
|
|
|
Ok(Some(tx.query_row(
|
|
r#"
|
|
SELECT user_id, user_name, created_at, last_message_at,
|
|
notifications, notifications_read_at
|
|
FROM contacts
|
|
WHERE storage_owner = ?1 AND user_id = ?2
|
|
"#,
|
|
params![storage_owner, user_id],
|
|
|row| {
|
|
Ok(Contact {
|
|
user_id: row.get(0)?,
|
|
user_name: row.get(1)?,
|
|
created_at: row.get(2)?,
|
|
last_message_at: row.get(3)?,
|
|
notifications: row.get(4)?,
|
|
notifications_read_at: row.get(5)?,
|
|
})
|
|
},
|
|
)?))
|
|
})
|
|
}
|