forked from jmug/stoatchat
New Feature: Add server-side channel unreads.
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/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
|
echo "pub const VERSION: &str = \"${version}\";" > src/version.rs
|
||||||
|
|||||||
@@ -128,6 +128,20 @@ impl Channel {
|
|||||||
operation: "delete_many",
|
operation: "delete_many",
|
||||||
with: "channel_invites",
|
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.
|
// Check if there are any attachments we need to delete.
|
||||||
let message_ids = messages
|
let message_ids = messages
|
||||||
|
|||||||
@@ -180,6 +180,8 @@ impl Server {
|
|||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ! FIXME: delete any unreads
|
||||||
|
|
||||||
for with in &["server_members", "server_bans"] {
|
for with in &["server_members", "server_bans"] {
|
||||||
get_collection(with)
|
get_collection(with)
|
||||||
.delete_many(
|
.delete_many(
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
pub type UserSettings = HashMap<String, (i64, String)>;
|
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>>,
|
||||||
|
}
|
||||||
|
|||||||
@@ -253,4 +253,23 @@ impl User {
|
|||||||
.flatten()
|
.flatten()
|
||||||
.collect::<Vec<String>>())
|
.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ pub async fn create_database() {
|
|||||||
.await
|
.await
|
||||||
.expect("Failed to create channel_invites collection.");
|
.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)
|
db.create_collection("migrations", None)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create migrations collection.");
|
.expect("Failed to create migrations collection.");
|
||||||
@@ -49,10 +53,6 @@ pub async fn create_database() {
|
|||||||
.await
|
.await
|
||||||
.expect("Failed to create attachments collection.");
|
.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)
|
db.create_collection("user_settings", None)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create user_settings collection.");
|
.expect("Failed to create user_settings collection.");
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ pub enum ClientboundNotification {
|
|||||||
Ready {
|
Ready {
|
||||||
users: Vec<User>,
|
users: Vec<User>,
|
||||||
servers: Vec<Server>,
|
servers: Vec<Server>,
|
||||||
channels: Vec<Channel>,
|
channels: Vec<Channel>
|
||||||
},
|
},
|
||||||
|
|
||||||
Message(Message),
|
Message(Message),
|
||||||
@@ -103,6 +103,11 @@ pub enum ClientboundNotification {
|
|||||||
id: String,
|
id: String,
|
||||||
user: String,
|
user: String,
|
||||||
},
|
},
|
||||||
|
ChannelAck {
|
||||||
|
id: String,
|
||||||
|
user: String,
|
||||||
|
message_id: String
|
||||||
|
},
|
||||||
|
|
||||||
ServerUpdate {
|
ServerUpdate {
|
||||||
id: String,
|
id: String,
|
||||||
|
|||||||
@@ -113,6 +113,6 @@ pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> {
|
|||||||
Ok(ClientboundNotification::Ready {
|
Ok(ClientboundNotification::Ready {
|
||||||
users,
|
users,
|
||||||
servers,
|
servers,
|
||||||
channels,
|
channels
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
// Block certain notifications from reaching users that aren't meant to see them.
|
||||||
match ¬ification {
|
match ¬ification {
|
||||||
ClientboundNotification::UserRelationship { id: user_id, .. }
|
ClientboundNotification::UserRelationship { id: user_id, .. }
|
||||||
| ClientboundNotification::UserSettingsUpdate { id: user_id, .. } => {
|
| ClientboundNotification::UserSettingsUpdate { id: user_id, .. }
|
||||||
|
| ClientboundNotification::ChannelAck { user: user_id, .. } => {
|
||||||
if &id != user_id {
|
if &id != user_id {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
53
src/routes/channels/channel_ack.rs
Normal file
53
src/routes/channels/channel_ack.rs
Normal 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(())
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
use rocket::Route;
|
use rocket::Route;
|
||||||
|
|
||||||
|
mod channel_ack;
|
||||||
mod delete_channel;
|
mod delete_channel;
|
||||||
mod edit_channel;
|
mod edit_channel;
|
||||||
mod fetch_channel;
|
mod fetch_channel;
|
||||||
@@ -18,6 +19,7 @@ mod message_send;
|
|||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
pub fn routes() -> Vec<Route> {
|
||||||
routes![
|
routes![
|
||||||
|
channel_ack::req,
|
||||||
fetch_channel::req,
|
fetch_channel::req,
|
||||||
fetch_members::req,
|
fetch_members::req,
|
||||||
delete_channel::req,
|
delete_channel::req,
|
||||||
|
|||||||
10
src/routes/sync/get_unreads.rs
Normal file
10
src/routes/sync/get_unreads.rs
Normal 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?))
|
||||||
|
}
|
||||||
@@ -2,7 +2,8 @@ use rocket::Route;
|
|||||||
|
|
||||||
mod get_settings;
|
mod get_settings;
|
||||||
mod set_settings;
|
mod set_settings;
|
||||||
|
mod get_unreads;
|
||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
pub fn routes() -> Vec<Route> {
|
||||||
routes![get_settings::req, set_settings::req]
|
routes![get_settings::req, set_settings::req, get_unreads::req]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
pub const VERSION: &str = "0.5.0-alpha.1";
|
pub const VERSION: &str = "0.5.0-alpha.3";
|
||||||
|
|||||||
Reference in New Issue
Block a user