From 84d09db9b3d072db0d77c48257947143f1cca125 Mon Sep 17 00:00:00 2001
From: Paul Makles <paulmakles@gmail.com>
Date: Tue, 29 Dec 2020 14:15:44 +0000
Subject: [PATCH] Add SavedMessages channel type, add create DM.

---
 src/database/entities/channel.rs | 22 +++++++++++++
 src/database/entities/user.rs    |  1 +
 src/database/guards/reference.rs |  2 +-
 src/database/permissions/mod.rs  |  4 +--
 src/routes/users/fetch_dms.rs    |  7 ++---
 src/routes/users/mod.rs          |  4 ++-
 src/routes/users/open_dm.rs      | 53 ++++++++++++++++++++++++++++++++
 7 files changed, 84 insertions(+), 9 deletions(-)
 create mode 100644 src/routes/users/open_dm.rs

diff --git a/src/database/entities/channel.rs b/src/database/entities/channel.rs
index 1f66d51..37736dc 100644
--- a/src/database/entities/channel.rs
+++ b/src/database/entities/channel.rs
@@ -1,4 +1,6 @@
 use serde::{Deserialize, Serialize};
+use crate::{database::get_collection, util::result::{Error, Result}};
+use mongodb::bson::to_document;
 
 /*#[derive(Serialize, Deserialize, Debug, Clone)]
 pub struct LastMessage {
@@ -33,6 +35,11 @@ pub struct Channel {
 #[derive(Serialize, Deserialize, Debug)]
 #[serde(tag = "type")]
 pub enum Channel {
+    SavedMessages {
+        #[serde(rename = "_id")]
+        id: String,
+        user: String
+    },
     DirectMessage {
         #[serde(rename = "_id")]
         id: String,
@@ -48,3 +55,18 @@ pub enum Channel {
         recipients: Vec<String>,
     }
 }
+
+impl Channel {
+    pub async fn save(&self) -> Result<()> {
+        get_collection("channels")
+            .insert_one(
+                to_document(&self)
+                    .map_err(|_| Error::DatabaseError { operation: "to_bson", with: "channel" })?,
+                None
+            )
+            .await
+            .map_err(|_| Error::DatabaseError { operation: "insert_one", with: "channel" })?;
+        
+        Ok(())
+    }
+}
diff --git a/src/database/entities/user.rs b/src/database/entities/user.rs
index 722c1bf..b55a8f8 100644
--- a/src/database/entities/user.rs
+++ b/src/database/entities/user.rs
@@ -7,6 +7,7 @@ use rocket::request::{self, FromRequest, Outcome, Request};
 
 #[derive(Serialize, Deserialize, Debug)]
 pub struct Relationship {
+    #[serde(rename = "_id")]
     pub id: String,
     pub status: u8,
 }
diff --git a/src/database/guards/reference.rs b/src/database/guards/reference.rs
index 1f9a36e..a4282a1 100644
--- a/src/database/guards/reference.rs
+++ b/src/database/guards/reference.rs
@@ -10,7 +10,7 @@ use validator::Validate;
 #[derive(Validate, Serialize, Deserialize)]
 pub struct Ref {
     #[validate(length(min = 26, max = 26))]
-    id: String,
+    pub id: String,
 }
 
 impl Ref {
diff --git a/src/database/permissions/mod.rs b/src/database/permissions/mod.rs
index 3b0f438..899aea7 100644
--- a/src/database/permissions/mod.rs
+++ b/src/database/permissions/mod.rs
@@ -18,8 +18,8 @@ bitfield! {
     pub get_invite, _: 29;
 }
 
-impl_op_ex!(+ |a: &UserPermission, b: &UserPermission| -> u32 { *a + *b });
-impl_op_ex!(+ |a: &u32, b: &UserPermission| -> u32 { *a + *b });
+impl_op_ex!(+ |a: &UserPermission, b: &UserPermission| -> u32 { *a as u32 | *b as u32 });
+impl_op_ex_commutative!(+ |a: &u32, b: &UserPermission| -> u32 { *a | *b as u32 });
 
 pub async fn temp_calc_perm(_user: &User, _target: &User) -> UserPermissions<[u32; 1]> {
     // if friends; Access + Message + Invite
diff --git a/src/routes/users/fetch_dms.rs b/src/routes/users/fetch_dms.rs
index beeb5e7..2eda25e 100644
--- a/src/routes/users/fetch_dms.rs
+++ b/src/routes/users/fetch_dms.rs
@@ -26,13 +26,10 @@ pub async fn req(user: User) -> Result<JsonValue> {
         .await
         .map_err(|_| Error::DatabaseError { operation: "find", with: "channels" })?;
     
-    let mut channels: Vec<Channel> = vec![];
+    let mut channels = vec![];
     while let Some(result) = cursor.next().await {
         if let Ok(doc) = result {
-            channels.push(
-                from_bson(Bson::Document(doc))
-                    .map_err(|_| Error::DatabaseError { operation: "from_bson", with: "channel" })?
-            );
+            channels.push(doc);
         }
     }
 
diff --git a/src/routes/users/mod.rs b/src/routes/users/mod.rs
index 7b92a3f..fd5b017 100644
--- a/src/routes/users/mod.rs
+++ b/src/routes/users/mod.rs
@@ -2,10 +2,12 @@ use rocket::Route;
 
 mod fetch_user;
 mod fetch_dms;
+mod open_dm;
 
 pub fn routes() -> Vec<Route> {
     routes! [
         fetch_user::req,
-        fetch_dms::req
+        fetch_dms::req,
+        open_dm::req,
     ]
 }
diff --git a/src/routes/users/open_dm.rs b/src/routes/users/open_dm.rs
new file mode 100644
index 0000000..32f7496
--- /dev/null
+++ b/src/routes/users/open_dm.rs
@@ -0,0 +1,53 @@
+use crate::database::entities::{Channel, User};
+use crate::database::guards::reference::Ref;
+use crate::util::result::{Error, Result};
+use crate::database::get_collection;
+use rocket_contrib::json::JsonValue;
+use mongodb::bson::doc;
+use ulid::Ulid;
+
+#[get("/<target>/dm")]
+pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
+    let query = if user.id == target.id {
+        doc! {
+            "type": "SavedMessages",
+            "user": &user.id
+        }
+    } else {
+        doc! {
+            "type": "DirectMessage",
+            "recipients": {
+                "$all": [ &user.id, &target.id ]
+            }
+        }
+    };
+
+    let existing_channel = get_collection("channels")
+        .find_one(query, None)
+        .await
+        .map_err(|_| Error::DatabaseError { operation: "find_one", with: "channel" })?;
+
+    if let Some(doc) = existing_channel {
+        Ok(json!(doc))
+    } else {
+        let id = Ulid::new().to_string();
+        let channel = if user.id == target.id {
+            Channel::SavedMessages {
+                id,
+                user: user.id
+            }
+        } else {
+            Channel::DirectMessage {
+                id,
+                active: false,
+                recipients: vec! [
+                    user.id,
+                    target.id
+                ]
+            }
+        };
+
+        channel.save().await?;
+        Ok(json!(channel))
+    }
+}
-- 
GitLab