diff --git a/set_version.sh b/set_version.sh index 4881d5162260a3f7fc50ae7e1d33c618c30db0f0..3b31b5d75b30e9b633122618d1a58a741bdaf1d8 100755 --- a/set_version.sh +++ b/set_version.sh @@ -1,3 +1,3 @@ #!/bin/bash -export version=0.5.0-alpha.2 +export version=0.5.0-alpha.3 echo "pub const VERSION: &str = \"${version}\";" > src/version.rs diff --git a/src/database/entities/channel.rs b/src/database/entities/channel.rs index 0ab74fc6bb23019769c7e10a904d61c9d9a79e2b..aefa613316a8d5e204a13d49ff2258eb89ae6baa 100644 --- a/src/database/entities/channel.rs +++ b/src/database/entities/channel.rs @@ -128,6 +128,20 @@ impl Channel { operation: "delete_many", with: "channel_invites", })?; + + // Delete any unreads. + get_collection("channel_unreads") + .delete_many( + doc! { + "_id.channel": id + }, + None, + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "delete_many", + with: "channel_unreads", + })?; // Check if there are any attachments we need to delete. let message_ids = messages diff --git a/src/database/entities/server.rs b/src/database/entities/server.rs index b30c5c4a7b0b64cf52ab49e878e857477c4fcb2a..5726d448b2c48ff859a5f27233567363a1d75a9c 100644 --- a/src/database/entities/server.rs +++ b/src/database/entities/server.rs @@ -180,6 +180,8 @@ impl Server { })?; } + // ! FIXME: delete any unreads + for with in &["server_members", "server_bans"] { get_collection(with) .delete_many( diff --git a/src/database/entities/sync.rs b/src/database/entities/sync.rs index d905c690a7125e1c84155f99fc59759d1d8003e4..07fb161d51f868fb9cc170f820d602913de1f6cb 100644 --- a/src/database/entities/sync.rs +++ b/src/database/entities/sync.rs @@ -1,3 +1,19 @@ use std::collections::HashMap; +use serde::{Serialize, Deserialize}; pub type UserSettings = HashMap<String, (i64, String)>; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ChannelCompositeKey { + pub channel: String, + pub user: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ChannelUnread { + #[serde(rename = "_id")] + pub id: ChannelCompositeKey, + + pub last_id: String, + pub mentions: Option<Vec<String>>, +} diff --git a/src/database/entities/user.rs b/src/database/entities/user.rs index d205ece7b855e3950aad3365406e3bba057aeb6f..b94d7a52be990c454dc020e6fc1412a61fe2af2d 100644 --- a/src/database/entities/user.rs +++ b/src/database/entities/user.rs @@ -253,4 +253,23 @@ impl User { .flatten() .collect::<Vec<String>>()) } + + /// Utility function to fetch unread objects for user. + pub async fn fetch_unreads(&self) -> Result<Vec<Document>> { + Ok(get_collection("channel_unreads") + .find( + doc! { + "_id.user": &self.id + }, + None + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "find_one", + with: "user_settings", + })? + .filter_map(async move |s| s.ok()) + .collect::<Vec<Document>>() + .await) + } } diff --git a/src/database/migrations/init.rs b/src/database/migrations/init.rs index dcb06308fdd23988967c766bb8b759b23ce34a62..1bb7277063d62aee17f7a2123674407465cd34bb 100644 --- a/src/database/migrations/init.rs +++ b/src/database/migrations/init.rs @@ -41,6 +41,10 @@ pub async fn create_database() { .await .expect("Failed to create channel_invites collection."); + db.create_collection("channel_unreads", None) + .await + .expect("Failed to create channel_unreads collection."); + db.create_collection("migrations", None) .await .expect("Failed to create migrations collection."); @@ -49,10 +53,6 @@ pub async fn create_database() { .await .expect("Failed to create attachments collection."); - db.create_collection("channel_unreads", None) - .await - .expect("Failed to create channel_unreads collection."); - db.create_collection("user_settings", None) .await .expect("Failed to create user_settings collection."); diff --git a/src/notifications/events.rs b/src/notifications/events.rs index 6ed1aaa897b8f59fc9eeda0258845aa9a1cbfbb4..6218b92e24892b8a4a55c9b719d84ef5517ed705 100644 --- a/src/notifications/events.rs +++ b/src/notifications/events.rs @@ -63,7 +63,7 @@ pub enum ClientboundNotification { Ready { users: Vec<User>, servers: Vec<Server>, - channels: Vec<Channel>, + channels: Vec<Channel> }, Message(Message), @@ -103,6 +103,11 @@ pub enum ClientboundNotification { id: String, user: String, }, + ChannelAck { + id: String, + user: String, + message_id: String + }, ServerUpdate { id: String, diff --git a/src/notifications/payload.rs b/src/notifications/payload.rs index ac5c45395b27e9ddeafd2a1be9aa892eb5985a44..6a279f0f873017e9ad65ee1778e852e099072969 100644 --- a/src/notifications/payload.rs +++ b/src/notifications/payload.rs @@ -113,6 +113,6 @@ pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> { Ok(ClientboundNotification::Ready { users, servers, - channels, + channels }) } diff --git a/src/notifications/websocket.rs b/src/notifications/websocket.rs index 6e275e1a97b20f459a6db5524710154fc266e85f..5046f777a1517dce077154b3966c1bbe14277033 100644 --- a/src/notifications/websocket.rs +++ b/src/notifications/websocket.rs @@ -244,7 +244,8 @@ pub fn publish(ids: Vec<String>, notification: ClientboundNotification) { // Block certain notifications from reaching users that aren't meant to see them. match ¬ification { ClientboundNotification::UserRelationship { id: user_id, .. } - | ClientboundNotification::UserSettingsUpdate { id: user_id, .. } => { + | ClientboundNotification::UserSettingsUpdate { id: user_id, .. } + | ClientboundNotification::ChannelAck { user: user_id, .. } => { if &id != user_id { continue; } diff --git a/src/routes/channels/channel_ack.rs b/src/routes/channels/channel_ack.rs new file mode 100644 index 0000000000000000000000000000000000000000..dfd45e457f34a90205d9d9a95478b3e35e648e0a --- /dev/null +++ b/src/routes/channels/channel_ack.rs @@ -0,0 +1,53 @@ +use crate::notifications::events::ClientboundNotification; +use crate::util::result::{Error, Result}; +use crate::database::*; + +use mongodb::bson::doc; +use mongodb::options::UpdateOptions; + +#[put("/<target>/ack/<message>")] +pub async fn req(user: User, target: Ref, message: Ref) -> Result<()> { + let target = target.fetch_channel().await?; + + let perm = permissions::PermissionCalculator::new(&user) + .with_channel(&target) + .for_channel() + .await?; + + if !perm.get_view() { + Err(Error::MissingPermission)? + } + + let id = target.id(); + get_collection("channel_unreads") + .update_one( + doc! { + "_id.channel": id, + "_id.user": &user.id + }, + doc! { + "$unset": { + "mentions": 1 + }, + "$set": { + "last_id": &message.id + } + }, + UpdateOptions::builder() + .upsert(true) + .build() + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "update_one", + with: "channel_unreads", + })?; + + ClientboundNotification::ChannelAck { + id: id.to_string(), + user: user.id.clone(), + message_id: message.id + }.publish(user.id); + + Ok(()) +} diff --git a/src/routes/channels/mod.rs b/src/routes/channels/mod.rs index cb7d0b4eea31e29bff3b67e403589808d3b27a44..ad2b70011b50d66d0d2c76dbf449c51a54e65427 100644 --- a/src/routes/channels/mod.rs +++ b/src/routes/channels/mod.rs @@ -1,5 +1,6 @@ use rocket::Route; +mod channel_ack; mod delete_channel; mod edit_channel; mod fetch_channel; @@ -18,6 +19,7 @@ mod message_send; pub fn routes() -> Vec<Route> { routes![ + channel_ack::req, fetch_channel::req, fetch_members::req, delete_channel::req, diff --git a/src/routes/sync/get_unreads.rs b/src/routes/sync/get_unreads.rs new file mode 100644 index 0000000000000000000000000000000000000000..4d59bebfbb403952d59ce4db2340d2c4df2e7254 --- /dev/null +++ b/src/routes/sync/get_unreads.rs @@ -0,0 +1,10 @@ +use crate::database::*; +use crate::util::result::Result; + +use rocket_contrib::json::JsonValue; +use mongodb::bson::doc; + +#[get("/unreads")] +pub async fn req(user: User) -> Result<JsonValue> { + Ok(json!(user.fetch_unreads().await?)) +} diff --git a/src/routes/sync/mod.rs b/src/routes/sync/mod.rs index 872fef1d6fae2e3f1a0dd65ded923eda738ad179..3bfe36d93f7453037cf8e6cf23be9b3780da3dd2 100644 --- a/src/routes/sync/mod.rs +++ b/src/routes/sync/mod.rs @@ -2,7 +2,8 @@ use rocket::Route; mod get_settings; mod set_settings; +mod get_unreads; pub fn routes() -> Vec<Route> { - routes![get_settings::req, set_settings::req] + routes![get_settings::req, set_settings::req, get_unreads::req] } diff --git a/src/version.rs b/src/version.rs index b8bf57cf8bcf060ad8f632a10031e3b9ec680b2b..133b97d198631cd5ac1261f15c4406a9507da78f 100644 --- a/src/version.rs +++ b/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "0.5.0-alpha.1"; +pub const VERSION: &str = "0.5.0-alpha.3";