add: manage followers page
This commit is contained in:
parent
959a125992
commit
70ecc6f96e
8 changed files with 119 additions and 6 deletions
|
@ -1798,8 +1798,8 @@
|
||||||
(span ("class" "notification chip") (text "{{ total }} votes"))
|
(span ("class" "notification chip") (text "{{ total }} votes"))
|
||||||
(text "{% if not poll[2] -%}")
|
(text "{% if not poll[2] -%}")
|
||||||
(span
|
(span
|
||||||
("class" "notification chip")
|
("class" "notification chip flex items-center gap-1")
|
||||||
(text "Expires in ")
|
(text "Expires in")
|
||||||
(span
|
(span
|
||||||
("class" "poll_date")
|
("class" "poll_date")
|
||||||
("data-created" "{{ poll[0].created }}")
|
("data-created" "{{ poll[0].created }}")
|
||||||
|
|
|
@ -62,12 +62,15 @@
|
||||||
("class" "card-nest")
|
("class" "card-nest")
|
||||||
(div
|
(div
|
||||||
("class" "card small flex items-center gap-2")
|
("class" "card small flex items-center gap-2")
|
||||||
(text "{{ icon \"user-plus\" }}")
|
(a
|
||||||
|
("href" "/api/v1/auth/user/find/{{ request.id }}")
|
||||||
|
(text "{{ components::avatar(username=request.id, selector_type=\"id\") }}"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"requests:label.user_follow_request\" }}")))
|
(text "{{ text \"requests:label.user_follow_request\" }}")))
|
||||||
(div
|
(div
|
||||||
("class" "card flex flex-col gap-2")
|
("class" "card flex flex-col gap-2")
|
||||||
(span
|
(span
|
||||||
|
("class" "flex items-center gap-2")
|
||||||
(text "{{ text \"requests:label.user_follow_request_message\" }}"))
|
(text "{{ text \"requests:label.user_follow_request_message\" }}"))
|
||||||
(div
|
(div
|
||||||
("class" "card flex flex-wrap w-full secondary gap-2")
|
("class" "card flex flex-wrap w-full secondary gap-2")
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
(text "{{ icon \"user-plus\" }}")
|
(text "{{ icon \"user-plus\" }}")
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"auth:action.request_to_follow\" }}")))
|
(text "{{ text \"auth:action.request_to_follow\" }}")))
|
||||||
|
(text "{% if follow_requested -%}")
|
||||||
(button
|
(button
|
||||||
("onclick" "cancel_follow_user(event)")
|
("onclick" "cancel_follow_user(event)")
|
||||||
("class" "lowered red{% if not follow_requested -%} hidden{%- endif %}")
|
("class" "lowered red{% if not follow_requested -%} hidden{%- endif %}")
|
||||||
|
@ -42,7 +43,7 @@
|
||||||
(text "{{ icon \"user-minus\" }}")
|
(text "{{ icon \"user-minus\" }}")
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"auth:action.cancel_follow_request\" }}")))
|
(text "{{ text \"auth:action.cancel_follow_request\" }}")))
|
||||||
(text "{% else %}")
|
(text "{%- endif %} {% else %}")
|
||||||
(button
|
(button
|
||||||
("onclick" "toggle_follow_user(event)")
|
("onclick" "toggle_follow_user(event)")
|
||||||
("class" "lowered red")
|
("class" "lowered red")
|
||||||
|
|
|
@ -137,6 +137,12 @@
|
||||||
(text "{{ icon \"rss\" }}")
|
(text "{{ icon \"rss\" }}")
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"auth:label.following\" }}")))
|
(text "{{ text \"auth:label.following\" }}")))
|
||||||
|
(a
|
||||||
|
("data-tab-button" "account/followers")
|
||||||
|
("href" "#/account/followers")
|
||||||
|
(text "{{ icon \"rss\" }}")
|
||||||
|
(span
|
||||||
|
(text "{{ text \"auth:label.followers\" }}")))
|
||||||
(a
|
(a
|
||||||
("data-tab-button" "account/blocks")
|
("data-tab-button" "account/blocks")
|
||||||
("href" "#/account/blocks")
|
("href" "#/account/blocks")
|
||||||
|
@ -457,7 +463,7 @@
|
||||||
(text "{{ icon \"external-link\" }}")
|
(text "{{ icon \"external-link\" }}")
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"requests:action.view_profile\" }}")))))
|
(text "{{ text \"requests:action.view_profile\" }}")))))
|
||||||
(text "{% endfor %}"))))
|
(text "{% endfor %} {{ components::pagination(page=page, items=following|length, key=\"#/account/following\") }}"))))
|
||||||
(script
|
(script
|
||||||
(text "globalThis.toggle_follow_user = async (uid) => {
|
(text "globalThis.toggle_follow_user = async (uid) => {
|
||||||
await trigger(\"atto::debounce\", [\"users::follow\"]);
|
await trigger(\"atto::debounce\", [\"users::follow\"]);
|
||||||
|
@ -473,6 +479,62 @@
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
};")))
|
};")))
|
||||||
|
(div
|
||||||
|
("class" "w-full flex flex-col gap-2 hidden")
|
||||||
|
("data-tab" "account/followers")
|
||||||
|
(div
|
||||||
|
("class" "card lowered flex flex-col gap-2")
|
||||||
|
(a
|
||||||
|
("href" "#/account")
|
||||||
|
("class" "button secondary")
|
||||||
|
(text "{{ icon \"arrow-left\" }}")
|
||||||
|
(span
|
||||||
|
(text "{{ text \"general:action.back\" }}")))
|
||||||
|
(div
|
||||||
|
("class" "card-nest")
|
||||||
|
(div
|
||||||
|
("class" "card flex items-center gap-2 small")
|
||||||
|
(text "{{ icon \"rss\" }}")
|
||||||
|
(span
|
||||||
|
(text "{{ text \"auth:label.followers\" }}")))
|
||||||
|
(div
|
||||||
|
("class" "card flex flex-col gap-2")
|
||||||
|
(text "{% for userfollow in followers %} {% set user = userfollow[1] %}")
|
||||||
|
(div
|
||||||
|
("class" "card secondary flex flex-wrap gap-2 items-center justify-between")
|
||||||
|
(div
|
||||||
|
("class" "flex gap-2")
|
||||||
|
(text "{{ components::avatar(username=user.username) }} {{ components::full_username(user=user) }}"))
|
||||||
|
(div
|
||||||
|
("class" "flex gap-2")
|
||||||
|
(button
|
||||||
|
("class" "lowered red small")
|
||||||
|
("onclick" "force_unfollow_me('{{ user.id }}')")
|
||||||
|
(text "{{ icon \"user-minus\" }}")
|
||||||
|
(span
|
||||||
|
(str (text "stacks:label.remove"))))
|
||||||
|
(a
|
||||||
|
("href" "/@{{ user.username }}")
|
||||||
|
("class" "button lowered small")
|
||||||
|
(text "{{ icon \"external-link\" }}")
|
||||||
|
(span
|
||||||
|
(text "{{ text \"requests:action.view_profile\" }}")))))
|
||||||
|
(text "{% endfor %} {{ components::pagination(page=page, items=following|length, key=\"#/account/followers\") }}"))))
|
||||||
|
(script
|
||||||
|
(text "globalThis.force_unfollow_me = async (uid) => {
|
||||||
|
await trigger(\"atto::debounce\", [\"users::follow\"]);
|
||||||
|
|
||||||
|
fetch(`/api/v1/auth/user/${uid}/force_unfollow_me`, {
|
||||||
|
method: \"POST\",
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((res) => {
|
||||||
|
trigger(\"atto::toast\", [
|
||||||
|
res.ok ? \"success\" : \"error\",
|
||||||
|
res.message,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
};")))
|
||||||
(div
|
(div
|
||||||
("class" "w-full flex flex-col gap-2 hidden")
|
("class" "w-full flex flex-col gap-2 hidden")
|
||||||
("data-tab" "account/blocks")
|
("data-tab" "account/blocks")
|
||||||
|
|
|
@ -154,6 +154,31 @@ pub async fn accept_follow_request(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn force_unfollow_me_request(
|
||||||
|
jar: CookieJar,
|
||||||
|
Path(id): Path<usize>,
|
||||||
|
Extension(data): Extension<State>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let data = &(data.read().await).0;
|
||||||
|
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageFollowing) {
|
||||||
|
Some(ua) => ua,
|
||||||
|
None => return Json(Error::NotAllowed.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(userfollow) = data.get_userfollow_by_receiver_initiator(user.id, id).await {
|
||||||
|
match data.delete_userfollow(userfollow.id, &user, false).await {
|
||||||
|
Ok(_) => Json(ApiReturn {
|
||||||
|
ok: true,
|
||||||
|
message: "User is no longer following you".to_string(),
|
||||||
|
payload: (),
|
||||||
|
}),
|
||||||
|
Err(e) => Json(e.into()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Json(Error::GeneralNotFound("user follow".to_string()).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Toggle blocking on the given user.
|
/// Toggle blocking on the given user.
|
||||||
pub async fn block_request(
|
pub async fn block_request(
|
||||||
jar: CookieJar,
|
jar: CookieJar,
|
||||||
|
|
|
@ -293,6 +293,10 @@ pub fn routes() -> Router {
|
||||||
"/auth/user/{id}/follow/accept",
|
"/auth/user/{id}/follow/accept",
|
||||||
post(auth::social::accept_follow_request),
|
post(auth::social::accept_follow_request),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/auth/user/{id}/force_unfollow_me",
|
||||||
|
post(auth::social::force_unfollow_me_request),
|
||||||
|
)
|
||||||
.route("/auth/user/{id}/block", post(auth::social::block_request))
|
.route("/auth/user/{id}/block", post(auth::social::block_request))
|
||||||
.route(
|
.route(
|
||||||
"/auth/user/{id}/block_ip",
|
"/auth/user/{id}/block_ip",
|
||||||
|
|
|
@ -63,11 +63,27 @@ pub async fn settings_request(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let followers = match data
|
||||||
|
.0
|
||||||
|
.fill_userfollows_with_initiator(
|
||||||
|
data.0
|
||||||
|
.get_userfollows_by_receiver(profile.id, 12, req.page)
|
||||||
|
.await
|
||||||
|
.unwrap_or(Vec::new()),
|
||||||
|
&None,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &None).await)),
|
||||||
|
};
|
||||||
|
|
||||||
let following = match data
|
let following = match data
|
||||||
.0
|
.0
|
||||||
.fill_userfollows_with_receiver(
|
.fill_userfollows_with_receiver(
|
||||||
data.0
|
data.0
|
||||||
.get_userfollows_by_initiator_all(profile.id)
|
.get_userfollows_by_initiator(profile.id, 12, req.page)
|
||||||
.await
|
.await
|
||||||
.unwrap_or(Vec::new()),
|
.unwrap_or(Vec::new()),
|
||||||
&None,
|
&None,
|
||||||
|
@ -138,6 +154,7 @@ pub async fn settings_request(
|
||||||
context.insert("page", &req.page);
|
context.insert("page", &req.page);
|
||||||
context.insert("uploads", &uploads);
|
context.insert("uploads", &uploads);
|
||||||
context.insert("stacks", &stacks);
|
context.insert("stacks", &stacks);
|
||||||
|
context.insert("followers", &followers);
|
||||||
context.insert("following", &following);
|
context.insert("following", &following);
|
||||||
context.insert("blocks", &blocks);
|
context.insert("blocks", &blocks);
|
||||||
context.insert("stackblocks", &stackblocks);
|
context.insert("stackblocks", &stackblocks);
|
||||||
|
|
1
justfile
1
justfile
|
@ -10,5 +10,6 @@ doc:
|
||||||
cargo doc --document-private-items --no-deps
|
cargo doc --document-private-items --no-deps
|
||||||
|
|
||||||
test:
|
test:
|
||||||
|
sudo pkill -e tetratto
|
||||||
cd example && LITTLEWEB=true PORT=4119 cargo run &
|
cd example && LITTLEWEB=true PORT=4119 cargo run &
|
||||||
cd example && cargo run
|
cd example && cargo run
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue