From 5da26cb833171f8e86a3a223889a1c51b29d044d Mon Sep 17 00:00:00 2001
From: Paul <paulmakles@gmail.com>
Date: Sat, 1 May 2021 15:54:29 +0100
Subject: [PATCH] Breaking: Provide new user object instead of id. Fix rustup
 complaining about join macros.

---
 .vscode/settings.json             |  5 +++++
 Cargo.lock                        |  2 +-
 Cargo.toml                        |  2 +-
 src/database/entities/user.rs     | 16 ++++++++++++++++
 src/database/permissions/mod.rs   |  9 +++++++++
 src/database/permissions/user.rs  |  2 +-
 src/notifications/events.rs       |  8 ++++----
 src/routes/root.rs                |  2 +-
 src/routes/users/add_friend.rs    | 15 +++++++++++----
 src/routes/users/block_user.rs    | 28 +++++++++++++++++++---------
 src/routes/users/remove_friend.rs | 14 ++++++++++----
 src/routes/users/unblock_user.rs  | 18 ++++++++++++------
 12 files changed, 90 insertions(+), 31 deletions(-)
 create mode 100644 .vscode/settings.json

diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..b458b99
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+    "rust-analyzer.diagnostics.disabled": [
+        "unresolved-macro-call"
+    ]
+}
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 612f500..20bc4ec 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2475,7 +2475,7 @@ dependencies = [
 
 [[package]]
 name = "revolt"
-version = "0.4.1-alpha.1"
+version = "0.4.1-alpha.2"
 dependencies = [
  "async-std",
  "async-tungstenite",
diff --git a/Cargo.toml b/Cargo.toml
index 0422bb7..ba34709 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "revolt"
-version = "0.4.1-alpha.1"
+version = "0.4.1-alpha.2"
 authors = ["Paul Makles <paulmakles@gmail.com>"]
 edition = "2018"
 
diff --git a/src/database/entities/user.rs b/src/database/entities/user.rs
index e7c0e61..165e9a1 100644
--- a/src/database/entities/user.rs
+++ b/src/database/entities/user.rs
@@ -82,6 +82,8 @@ pub struct User {
 impl User {
     /// Mutate the user object to include relationship as seen by user.
     pub fn from(mut self, user: &User) -> User {
+        self.relationship = Some(RelationshipStatus::None);
+
         if self.id == user.id {
             self.relationship = Some(RelationshipStatus::User);
             return self;
@@ -102,12 +104,26 @@ impl User {
     pub fn with(mut self, permissions: UserPermissions<[u32; 1]>) -> User {
         if permissions.get_view_profile() {
             self.online = Some(is_online(&self.id));
+        } else {
+            self.status = None;
         }
 
         self.profile = None;
         self
     }
 
+    /// Mutate the user object to appear as seen by user.
+    /// Also overrides the relationship status.
+    pub async fn from_override(mut self, user: &User, relationship: RelationshipStatus) -> Result<User> {
+        let permissions = PermissionCalculator::new(&user)
+            .with_relationship(&relationship)
+            .for_user(&self.id).await?;
+
+        self.relations = None;
+        self.relationship = Some(relationship);
+        Ok(self.with(permissions))
+    }
+
     /// Utility function for checking claimed usernames.
     pub async fn is_username_taken(username: &str) -> Result<bool> {
         if username.to_lowercase() == "revolt" && username.to_lowercase() == "admin" {
diff --git a/src/database/permissions/mod.rs b/src/database/permissions/mod.rs
index 7fc9b26..6164478 100644
--- a/src/database/permissions/mod.rs
+++ b/src/database/permissions/mod.rs
@@ -9,6 +9,7 @@ pub struct PermissionCalculator<'a> {
     perspective: &'a User,
 
     user: Option<&'a User>,
+    relationship: Option<&'a RelationshipStatus>,
     channel: Option<&'a Channel>,
 
     has_mutual_connection: bool,
@@ -20,6 +21,7 @@ impl<'a> PermissionCalculator<'a> {
             perspective,
 
             user: None,
+            relationship: None,
             channel: None,
 
             has_mutual_connection: false,
@@ -33,6 +35,13 @@ impl<'a> PermissionCalculator<'a> {
         }
     }
 
+    pub fn with_relationship(self, relationship: &'a RelationshipStatus) -> PermissionCalculator {
+        PermissionCalculator {
+            relationship: Some(&relationship),
+            ..self
+        }
+    }
+
     pub fn with_channel(self, channel: &'a Channel) -> PermissionCalculator {
         PermissionCalculator {
             channel: Some(&channel),
diff --git a/src/database/permissions/user.rs b/src/database/permissions/user.rs
index 7d516ee..f4d7c58 100644
--- a/src/database/permissions/user.rs
+++ b/src/database/permissions/user.rs
@@ -49,7 +49,7 @@ impl<'a> PermissionCalculator<'a> {
         }
 
         let mut permissions: u32 = 0;
-        match get_relationship(&self.perspective, &target) {
+        match self.relationship.clone().map(|v| v.to_owned()).unwrap_or_else(|| get_relationship(&self.perspective, &target)) {
             RelationshipStatus::Friend => return Ok(u32::MAX),
             RelationshipStatus::Blocked | RelationshipStatus::BlockedOther => {
                 return Ok(UserPermission::Access as u32)
diff --git a/src/notifications/events.rs b/src/notifications/events.rs
index 4e92bdb..a71f0ae 100644
--- a/src/notifications/events.rs
+++ b/src/notifications/events.rs
@@ -80,8 +80,8 @@ pub enum ClientboundNotification {
     },
     UserRelationship {
         id: String,
-        user: String,
-        status: RelationshipStatus,
+        user: User,
+        status: RelationshipStatus
     },
     UserPresence {
         id: String,
@@ -116,7 +116,7 @@ pub fn prehandle_hook(notification: &ClientboundNotification) {
         }
         ClientboundNotification::UserRelationship { id, user, status } => {
             if status != &RelationshipStatus::None {
-                subscribe_if_exists(id.clone(), user.clone()).ok();
+                subscribe_if_exists(id.clone(), user.id.clone()).ok();
             }
         }
         _ => {}
@@ -132,7 +132,7 @@ pub fn posthandle_hook(notification: &ClientboundNotification) {
             if status == &RelationshipStatus::None {
                 get_hive()
                     .hive
-                    .unsubscribe(&id.to_string(), &user.to_string())
+                    .unsubscribe(&id.to_string(), &user.id.to_string())
                     .ok();
             }
         }
diff --git a/src/routes/root.rs b/src/routes/root.rs
index 1f2be02..669b08c 100644
--- a/src/routes/root.rs
+++ b/src/routes/root.rs
@@ -9,7 +9,7 @@ use rocket_contrib::json::JsonValue;
 #[get("/")]
 pub async fn root() -> JsonValue {
     json!({
-        "revolt": "0.4.1-alpha.1",
+        "revolt": "0.4.1-alpha.2",
         "features": {
             "registration": !*DISABLE_REGISTRATION,
             "captcha": {
diff --git a/src/routes/users/add_friend.rs b/src/routes/users/add_friend.rs
index f918fc8..8d5cbef 100644
--- a/src/routes/users/add_friend.rs
+++ b/src/routes/users/add_friend.rs
@@ -31,6 +31,8 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> {
         with: "user",
     })?;
 
+    let target_user = Ref::from(target_id.to_string())?.fetch_user().await?;
+
     match get_relationship(&user, &target_id) {
         RelationshipStatus::User => return Err(Error::NoEffect),
         RelationshipStatus::Friend => return Err(Error::AlreadyFriends),
@@ -65,16 +67,19 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> {
                 )
             ) {
                 Ok(_) => {
+                    let target_user = target_user.from_override(&user, RelationshipStatus::Friend).await?;
+                    let user = user.from_override(&target_user, RelationshipStatus::Friend).await?;
+
                     try_join!(
                         ClientboundNotification::UserRelationship {
                             id: user.id.clone(),
-                            user: target_id.to_string(),
+                            user: target_user,
                             status: RelationshipStatus::Friend
                         }
                         .publish(user.id.clone()),
                         ClientboundNotification::UserRelationship {
                             id: target_id.to_string(),
-                            user: user.id.clone(),
+                            user,
                             status: RelationshipStatus::Friend
                         }
                         .publish(target_id.to_string())
@@ -121,16 +126,18 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> {
                 )
             ) {
                 Ok(_) => {
+                    let target_user = target_user.from_override(&user, RelationshipStatus::Outgoing).await?;
+                    let user = user.from_override(&target_user, RelationshipStatus::Incoming).await?;
                     try_join!(
                         ClientboundNotification::UserRelationship {
                             id: user.id.clone(),
-                            user: target_id.to_string(),
+                            user: target_user,
                             status: RelationshipStatus::Outgoing
                         }
                         .publish(user.id.clone()),
                         ClientboundNotification::UserRelationship {
                             id: target_id.to_string(),
-                            user: user.id.clone(),
+                            user,
                             status: RelationshipStatus::Incoming
                         }
                         .publish(target_id.to_string())
diff --git a/src/routes/users/block_user.rs b/src/routes/users/block_user.rs
index 1fb5183..a554c28 100644
--- a/src/routes/users/block_user.rs
+++ b/src/routes/users/block_user.rs
@@ -10,6 +10,8 @@ use rocket_contrib::json::JsonValue;
 pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
     let col = get_collection("users");
 
+    let target = target.fetch_user().await?;
+
     match get_relationship(&user, &target.id) {
         RelationshipStatus::User | RelationshipStatus::Blocked => Err(Error::NoEffect),
         RelationshipStatus::BlockedOther => {
@@ -33,7 +35,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
 
             ClientboundNotification::UserRelationship {
                 id: user.id.clone(),
-                user: target.id.clone(),
+                user: target,
                 status: RelationshipStatus::Blocked,
             }
             .publish(user.id.clone())
@@ -74,19 +76,23 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                 )
             ) {
                 Ok(_) => {
+                    let target = target.from_override(&user, RelationshipStatus::Friend).await?;
+                    let user = user.from_override(&target, RelationshipStatus::Friend).await?;
+                    let target_id = target.id.clone();
+
                     try_join!(
                         ClientboundNotification::UserRelationship {
                             id: user.id.clone(),
-                            user: target.id.clone(),
+                            user: target,
                             status: RelationshipStatus::Blocked
                         }
                         .publish(user.id.clone()),
                         ClientboundNotification::UserRelationship {
-                            id: target.id.clone(),
-                            user: user.id.clone(),
+                            id: target_id.clone(),
+                            user,
                             status: RelationshipStatus::BlockedOther
                         }
-                        .publish(target.id.clone())
+                        .publish(target_id)
                     )
                     .ok();
 
@@ -128,19 +134,23 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                 )
             ) {
                 Ok(_) => {
+                    let target = target.from_override(&user, RelationshipStatus::Blocked).await?;
+                    let user = user.from_override(&target, RelationshipStatus::BlockedOther).await?;
+                    let target_id = target.id.clone();
+
                     try_join!(
                         ClientboundNotification::UserRelationship {
                             id: user.id.clone(),
-                            user: target.id.clone(),
+                            user: target,
                             status: RelationshipStatus::Blocked
                         }
                         .publish(user.id.clone()),
                         ClientboundNotification::UserRelationship {
-                            id: target.id.clone(),
-                            user: user.id.clone(),
+                            id: target_id.clone(),
+                            user,
                             status: RelationshipStatus::BlockedOther
                         }
-                        .publish(target.id.clone())
+                        .publish(target_id)
                     )
                     .ok();
 
diff --git a/src/routes/users/remove_friend.rs b/src/routes/users/remove_friend.rs
index 37e831a..882594d 100644
--- a/src/routes/users/remove_friend.rs
+++ b/src/routes/users/remove_friend.rs
@@ -10,6 +10,8 @@ use rocket_contrib::json::JsonValue;
 pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
     let col = get_collection("users");
 
+    let target = target.fetch_user().await?;
+
     match get_relationship(&user, &target.id) {
         RelationshipStatus::Friend
         | RelationshipStatus::Outgoing
@@ -43,19 +45,23 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                 )
             ) {
                 Ok(_) => {
+                    let target = target.from_override(&user, RelationshipStatus::None).await?;
+                    let user = user.from_override(&target, RelationshipStatus::None).await?;
+                    let target_id = target.id.clone();
+
                     try_join!(
                         ClientboundNotification::UserRelationship {
                             id: user.id.clone(),
-                            user: target.id.clone(),
+                            user: target,
                             status: RelationshipStatus::None
                         }
                         .publish(user.id.clone()),
                         ClientboundNotification::UserRelationship {
-                            id: target.id.clone(),
-                            user: user.id.clone(),
+                            id: target_id.clone(),
+                            user,
                             status: RelationshipStatus::None
                         }
-                        .publish(target.id.clone())
+                        .publish(target_id)
                     )
                     .ok();
 
diff --git a/src/routes/users/unblock_user.rs b/src/routes/users/unblock_user.rs
index ec575c4..8be4303 100644
--- a/src/routes/users/unblock_user.rs
+++ b/src/routes/users/unblock_user.rs
@@ -9,10 +9,11 @@ use rocket_contrib::json::JsonValue;
 #[delete("/<target>/block")]
 pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
     let col = get_collection("users");
+    let target = target.fetch_user().await?;
 
     match get_relationship(&user, &target.id) {
         RelationshipStatus::Blocked => {
-            match get_relationship(&target.fetch_user().await?, &user.id) {
+            match get_relationship(&target, &user.id) {
                 RelationshipStatus::Blocked => {
                     col.update_one(
                         doc! {
@@ -32,9 +33,10 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                         with: "user",
                     })?;
 
+                    let target = target.from_override(&user, RelationshipStatus::BlockedOther).await?;
                     ClientboundNotification::UserRelationship {
                         id: user.id.clone(),
-                        user: target.id.clone(),
+                        user: target,
                         status: RelationshipStatus::BlockedOther,
                     }
                     .publish(user.id.clone())
@@ -73,19 +75,23 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                         )
                     ) {
                         Ok(_) => {
+                            let target = target.from_override(&user, RelationshipStatus::None).await?;
+                            let user = user.from_override(&target, RelationshipStatus::None).await?;
+                            let target_id = target.id.clone();
+
                             try_join!(
                                 ClientboundNotification::UserRelationship {
                                     id: user.id.clone(),
-                                    user: target.id.clone(),
+                                    user: target,
                                     status: RelationshipStatus::None
                                 }
                                 .publish(user.id.clone()),
                                 ClientboundNotification::UserRelationship {
-                                    id: target.id.clone(),
-                                    user: user.id.clone(),
+                                    id: target_id.clone(),
+                                    user: user,
                                     status: RelationshipStatus::None
                                 }
-                                .publish(target.id.clone())
+                                .publish(target_id)
                             )
                             .ok();
 
-- 
GitLab