New Feature: Add server-side channel unreads.

This commit is contained in:
Paul
2021-06-16 23:24:31 +01:00
parent d2b17b64e7
commit 4727f997ed
14 changed files with 133 additions and 10 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -180,6 +180,8 @@ impl Server {
})?;
}
// ! FIXME: delete any unreads
for with in &["server_members", "server_bans"] {
get_collection(with)
.delete_many(

View File

@@ -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>>,
}

View File

@@ -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)
}
}

View File

@@ -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.");

View File

@@ -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,

View File

@@ -113,6 +113,6 @@ pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> {
Ok(ClientboundNotification::Ready {
users,
servers,
channels,
channels
})
}

View File

@@ -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 &notification {
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;
}

View File

@@ -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(())
}

View File

@@ -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,

View File

@@ -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?))
}

View File

@@ -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]
}

View File

@@ -1 +1 @@
pub const VERSION: &str = "0.5.0-alpha.1";
pub const VERSION: &str = "0.5.0-alpha.3";