diff --git a/crates/app/src/public/html/post/post.lisp b/crates/app/src/public/html/post/post.lisp
index b43fc82..81a16a9 100644
--- a/crates/app/src/public/html/post/post.lisp
+++ b/crates/app/src/public/html/post/post.lisp
@@ -71,7 +71,6 @@
("name" "content")
("id" "content")
("placeholder" "content")
- ("required" "")
("minlength" "2")
("maxlength" "4096")))
(div
diff --git a/crates/app/src/public/html/profile/base.lisp b/crates/app/src/public/html/profile/base.lisp
index c482033..51f8489 100644
--- a/crates/app/src/public/html/profile/base.lisp
+++ b/crates/app/src/public/html/profile/base.lisp
@@ -290,7 +290,7 @@
]);
fetch(
- \"/api/v1/auth/user/{{ profile.id }}/follow\",
+ \"/api/v1/auth/user/{{ profile.id }}/follow/toggle\",
{
method: \"POST\",
},
diff --git a/crates/app/src/public/html/profile/private.lisp b/crates/app/src/public/html/profile/private.lisp
index 8bd94e9..4654298 100644
--- a/crates/app/src/public/html/profile/private.lisp
+++ b/crates/app/src/public/html/profile/private.lisp
@@ -81,7 +81,7 @@
(script
(text "globalThis.toggle_follow_user = async (e) => {
await trigger(\"atto::debounce\", [\"users::follow\"]);
- fetch(\"/api/v1/auth/user/{{ profile.id }}/follow\", {
+ fetch(\"/api/v1/auth/user/{{ profile.id }}/follow/toggle\", {
method: \"POST\",
})
.then((res) => res.json())
diff --git a/crates/app/src/public/js/me.js b/crates/app/src/public/js/me.js
index e7fa2d6..99fda4e 100644
--- a/crates/app/src/public/js/me.js
+++ b/crates/app/src/public/js/me.js
@@ -193,9 +193,13 @@
like.classList.add("green");
like.querySelector("svg").classList.add("filled");
- dislike.classList.remove("red");
+ if (dislike) {
+ dislike.classList.remove("red");
+ }
} else {
- dislike.classList.add("red");
+ if (dislike) {
+ dislike.classList.add("red");
+ }
like.classList.remove("green");
like.querySelector("svg").classList.remove("filled");
diff --git a/crates/app/src/routes/api/v1/auth/social.rs b/crates/app/src/routes/api/v1/auth/social.rs
index 17ca6cf..86a601d 100644
--- a/crates/app/src/routes/api/v1/auth/social.rs
+++ b/crates/app/src/routes/api/v1/auth/social.rs
@@ -17,7 +17,7 @@ use tetratto_core::model::{
};
/// Toggle following on the given user.
-pub async fn follow_request(
+pub async fn toggle_follow_request(
jar: CookieJar,
Path(id): Path,
Extension(data): Extension,
@@ -154,6 +154,71 @@ pub async fn accept_follow_request(
}
}
+pub async fn follow_request(
+ jar: CookieJar,
+ Path(id): Path,
+ Extension(data): Extension,
+) -> impl IntoResponse {
+ let data = &(data.read().await).0;
+ let mut user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageFollowing) {
+ Some(ua) => ua,
+ None => return Json(Error::NotAllowed.into()),
+ };
+
+ if data
+ .get_userfollow_by_initiator_receiver(user.id, id)
+ .await
+ .is_ok()
+ {
+ return Json(Error::MiscError("Already following user".to_string()).into());
+ } else {
+ match data
+ .create_userfollow(UserFollow::new(user.id, id), &user, false)
+ .await
+ {
+ Ok(r) => {
+ if r == FollowResult::Followed {
+ if let Err(e) = data
+ .create_notification(Notification::new(
+ "Somebody has followed you!".to_string(),
+ format!(
+ "You have been followed by [@{}](/api/v1/auth/user/find/{}).",
+ user.username, user.id
+ ),
+ id,
+ ))
+ .await
+ {
+ return Json(e.into());
+ };
+
+ // award achievement
+ if let Err(e) = data
+ .add_achievement(&mut user, AchievementName::FollowUser.into(), true)
+ .await
+ {
+ return Json(e.into());
+ }
+
+ // ...
+ Json(ApiReturn {
+ ok: true,
+ message: "User followed".to_string(),
+ payload: (),
+ })
+ } else {
+ Json(ApiReturn {
+ ok: true,
+ message: "Asked to follow user".to_string(),
+ payload: (),
+ })
+ }
+ }
+ Err(e) => Json(e.into()),
+ }
+ }
+}
+
pub async fn force_unfollow_me_request(
jar: CookieJar,
Path(id): Path,
diff --git a/crates/app/src/routes/api/v1/mod.rs b/crates/app/src/routes/api/v1/mod.rs
index 0fbfc40..d59fe26 100644
--- a/crates/app/src/routes/api/v1/mod.rs
+++ b/crates/app/src/routes/api/v1/mod.rs
@@ -285,6 +285,10 @@ pub fn routes() -> Router {
.route("/auth/user/{id}/avatar", get(auth::images::avatar_request))
.route("/auth/user/{id}/banner", get(auth::images::banner_request))
.route("/auth/user/{id}/follow", post(auth::social::follow_request))
+ .route(
+ "/auth/user/{id}/follow/toggle",
+ post(auth::social::toggle_follow_request),
+ )
.route(
"/auth/user/{id}/follow/cancel",
post(auth::social::cancel_follow_request),
diff --git a/justfile b/justfile
index 56aa26b..a83d0c4 100644
--- a/justfile
+++ b/justfile
@@ -10,6 +10,5 @@ doc:
cargo doc --document-private-items --no-deps
test:
- sudo pkill -e tetratto
cd example && LITTLEWEB=true PORT=4119 cargo run &
cd example && cargo run