From 4727f997edb31abed570820b0026362341c44c96 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 16 Jun 2021 23:24:31 +0100 Subject: [PATCH] New Feature: Add server-side channel unreads. --- set_version.sh | 2 +- src/database/entities/channel.rs | 14 ++++++++ src/database/entities/server.rs | 2 ++ src/database/entities/sync.rs | 16 +++++++++ src/database/entities/user.rs | 19 +++++++++++ src/database/migrations/init.rs | 8 ++--- src/notifications/events.rs | 7 +++- src/notifications/payload.rs | 2 +- src/notifications/websocket.rs | 3 +- src/routes/channels/channel_ack.rs | 53 ++++++++++++++++++++++++++++++ src/routes/channels/mod.rs | 2 ++ src/routes/sync/get_unreads.rs | 10 ++++++ src/routes/sync/mod.rs | 3 +- src/version.rs | 2 +- 14 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 src/routes/channels/channel_ack.rs create mode 100644 src/routes/sync/get_unreads.rs diff --git a/set_version.sh b/set_version.sh index 4881d516..3b31b5d7 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 0ab74fc6..aefa6133 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 b30c5c4a..5726d448 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 d905c690..07fb161d 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; + +#[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>, +} diff --git a/src/database/entities/user.rs b/src/database/entities/user.rs index d205ece7..b94d7a52 100644 --- a/src/database/entities/user.rs +++ b/src/database/entities/user.rs @@ -253,4 +253,23 @@ impl User { .flatten() .collect::>()) } + + /// Utility function to fetch unread objects for user. + pub async fn fetch_unreads(&self) -> Result> { + 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::>() + .await) + } } diff --git a/src/database/migrations/init.rs b/src/database/migrations/init.rs index dcb06308..1bb72770 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 6ed1aaa8..6218b92e 100644 --- a/src/notifications/events.rs +++ b/src/notifications/events.rs @@ -63,7 +63,7 @@ pub enum ClientboundNotification { Ready { users: Vec, servers: Vec, - channels: Vec, + channels: Vec }, 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 ac5c4539..6a279f0f 100644 --- a/src/notifications/payload.rs +++ b/src/notifications/payload.rs @@ -113,6 +113,6 @@ pub async fn generate_ready(mut user: User) -> Result { Ok(ClientboundNotification::Ready { users, servers, - channels, + channels }) } diff --git a/src/notifications/websocket.rs b/src/notifications/websocket.rs index 6e275e1a..5046f777 100644 --- a/src/notifications/websocket.rs +++ b/src/notifications/websocket.rs @@ -244,7 +244,8 @@ pub fn publish(ids: Vec, 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 00000000..dfd45e45 --- /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("//ack/")] +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 cb7d0b4e..ad2b7001 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 { 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 00000000..4d59bebf --- /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 { + Ok(json!(user.fetch_unreads().await?)) +} diff --git a/src/routes/sync/mod.rs b/src/routes/sync/mod.rs index 872fef1d..3bfe36d9 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 { - 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 b8bf57cf..133b97d1 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";