diff --git a/crates/app/src/langs/en-US.toml b/crates/app/src/langs/en-US.toml
index 48bb07f..3c9556c 100644
--- a/crates/app/src/langs/en-US.toml
+++ b/crates/app/src/langs/en-US.toml
@@ -22,10 +22,13 @@ version = "1.0.0"
"auth:action.logout" = "Logout"
"auto:action.follow" = "Follow"
"auto:action.unfollow" = "Unfollow"
+"auto:action.block" = "Block"
+"auto:action.unblock" = "Unblock"
"auth:link.my_profile" = "My profile"
"auth:link.settings" = "Settings"
"auth:label.followers" = "Followers"
"auth:label.following" = "Following"
+"auth:label.relationship" = "Relationship"
"auth:label.joined_communities" = "Joined communities"
"auth:label.recent_posts" = "Recent posts"
diff --git a/crates/app/src/public/css/style.css b/crates/app/src/public/css/style.css
index 41e9f50..5ba777c 100644
--- a/crates/app/src/public/css/style.css
+++ b/crates/app/src/public/css/style.css
@@ -85,7 +85,7 @@ main {
}
article {
- margin-top: 1rem;
+ margin: 1rem 0;
}
@media screen and (max-width: 900px) {
diff --git a/crates/app/src/public/html/communities/settings.html b/crates/app/src/public/html/communities/settings.html
index 911fa7c..fb15aeb 100644
--- a/crates/app/src/public/html/communities/settings.html
+++ b/crates/app/src/public/html/communities/settings.html
@@ -25,9 +25,24 @@
{{ icon "users-round" }}
diff --git a/crates/app/src/public/js/atto.js b/crates/app/src/public/js/atto.js
index 4c48fbf..ea3985a 100644
--- a/crates/app/src/public/js/atto.js
+++ b/crates/app/src/public/js/atto.js
@@ -34,6 +34,10 @@ media_theme_pref();
(() => {
const self = reg_ns("atto");
+ for (const element of document.querySelectorAll('[selected="false"]')) {
+ element.removeAttribute("selected");
+ }
+
// init
use("me", () => {});
diff --git a/crates/app/src/routes/api/v1/auth/social.rs b/crates/app/src/routes/api/v1/auth/social.rs
index 8692875..fd68a3a 100644
--- a/crates/app/src/routes/api/v1/auth/social.rs
+++ b/crates/app/src/routes/api/v1/auth/social.rs
@@ -4,7 +4,7 @@ use crate::{
};
use axum::{Extension, Json, extract::Path, response::IntoResponse};
use axum_extra::extract::CookieJar;
-use tetratto_core::model::auth::{UserBlock, UserFollow};
+use tetratto_core::model::auth::{Notification, UserBlock, UserFollow};
/// Toggle following on the given user.
pub async fn follow_request(
@@ -20,7 +20,7 @@ pub async fn follow_request(
if let Ok(userfollow) = data.get_userfollow_by_initiator_receiver(user.id, id).await {
// delete
- match data.delete_userfollow(userfollow.id, user).await {
+ match data.delete_userfollow(userfollow.id, &user).await {
Ok(_) => Json(ApiReturn {
ok: true,
message: "User unfollowed".to_string(),
@@ -31,11 +31,27 @@ pub async fn follow_request(
} else {
// create
match data.create_userfollow(UserFollow::new(user.id, id)).await {
- Ok(_) => Json(ApiReturn {
- ok: true,
- message: "User followed".to_string(),
- payload: (),
- }),
+ Ok(_) => {
+ if let Err(e) = data
+ .create_notification(Notification::new(
+ "Somebody has followed you!".to_string(),
+ format!(
+ "You have been followed by [@{}](/api/v1/auth/profile/find/{}).",
+ user.username, user.id
+ ),
+ id,
+ ))
+ .await
+ {
+ return Json(e.into());
+ };
+
+ Json(ApiReturn {
+ ok: true,
+ message: "User followed".to_string(),
+ payload: (),
+ })
+ }
Err(e) => Json(e.into()),
}
}
@@ -70,10 +86,22 @@ pub async fn block_request(
if let Ok(userfollow) = data.get_userfollow_by_initiator_receiver(user.id, id).await
{
// automatically unfollow
- match data.delete_userfollow(userfollow.id, user).await {
+ match data.delete_userfollow(userfollow.id, &user).await {
Ok(_) => Json(ApiReturn {
ok: true,
- message: "User unfollowed".to_string(),
+ message: "User blocked".to_string(),
+ payload: (),
+ }),
+ Err(e) => Json(e.into()),
+ }
+ } else if let Ok(userfollow) =
+ data.get_userfollow_by_receiver_initiator(user.id, id).await
+ {
+ // automatically unfollow
+ match data.delete_userfollow(userfollow.id, &user).await {
+ Ok(_) => Json(ApiReturn {
+ ok: true,
+ message: "User blocked".to_string(),
payload: (),
}),
Err(e) => Json(e.into()),
diff --git a/crates/app/src/routes/pages/profile.rs b/crates/app/src/routes/pages/profile.rs
index 6b56d78..9a9aa7e 100644
--- a/crates/app/src/routes/pages/profile.rs
+++ b/crates/app/src/routes/pages/profile.rs
@@ -55,11 +55,15 @@ pub fn profile_context(
communities: &Vec,
is_self: bool,
is_following: bool,
+ is_following_you: bool,
+ is_blocking: bool,
) {
context.insert("profile", &profile);
context.insert("communities", &communities);
context.insert("is_self", &is_self);
context.insert("is_following", &is_following);
+ context.insert("is_following_you", &is_following_you);
+ context.insert("is_blocking", &is_blocking);
}
/// `/user/{username}`
@@ -94,15 +98,17 @@ pub async fn posts_request(
// check for private profile
if other_user.settings.private_profile {
if let Some(ref ua) = user {
- if data
- .0
- .get_userfollow_by_initiator_receiver(other_user.id, ua.id)
- .await
- .is_err()
- {
- return Err(Html(
- render_error(Error::NotAllowed, &jar, &data, &user).await,
- ));
+ if ua.id != other_user.id {
+ if data
+ .0
+ .get_userfollow_by_initiator_receiver(other_user.id, ua.id)
+ .await
+ .is_err()
+ {
+ return Err(Html(
+ render_error(Error::NotAllowed, &jar, &data, &user).await,
+ ));
+ }
}
} else {
return Err(Html(
@@ -151,6 +157,24 @@ pub async fn posts_request(
false
};
+ let is_following_you = if let Some(ref ua) = user {
+ data.0
+ .get_userfollow_by_receiver_initiator(ua.id, other_user.id)
+ .await
+ .is_ok()
+ } else {
+ false
+ };
+
+ let is_blocking = if let Some(ref ua) = user {
+ data.0
+ .get_userblock_by_initiator_receiver(ua.id, other_user.id)
+ .await
+ .is_ok()
+ } else {
+ false
+ };
+
context.insert("posts", &posts);
profile_context(
&mut context,
@@ -158,6 +182,8 @@ pub async fn posts_request(
&communities,
is_self,
is_following,
+ is_following_you,
+ is_blocking,
);
// return
diff --git a/crates/core/src/database/userfollows.rs b/crates/core/src/database/userfollows.rs
index 1a65811..f3d51ba 100644
--- a/crates/core/src/database/userfollows.rs
+++ b/crates/core/src/database/userfollows.rs
@@ -181,7 +181,7 @@ impl DataManager {
Ok(())
}
- pub async fn delete_userfollow(&self, id: usize, user: User) -> Result<()> {
+ pub async fn delete_userfollow(&self, id: usize, user: &User) -> Result<()> {
let follow = self.get_userfollow_by_id(id).await?;
if (user.id != follow.initiator) && (user.id != follow.receiver) {
@@ -208,11 +208,11 @@ impl DataManager {
self.2.remove(format!("atto.userfollow:{}", id)).await;
// decr counts
- self.incr_user_following_count(follow.initiator)
+ self.decr_user_following_count(follow.initiator)
.await
.unwrap();
- self.incr_user_follower_count(follow.receiver)
+ self.decr_user_follower_count(follow.receiver)
.await
.unwrap();
{{ profile.follower_count }}
- {{ text "auth:label.followers" }} - - -{{ profile.following_count }}
- {{ text "auth:label.following" }} - +{{ profile.follower_count }}
+ {{ text "auth:label.followers" }} + + +{{ profile.following_count }}
+ {{ text "auth:label.following" }} + +