add: followers/following ui
This commit is contained in:
parent
17564ede49
commit
e183a01887
13 changed files with 469 additions and 86 deletions
|
@ -42,6 +42,8 @@ pub const AUTH_REGISTER: &str = include_str!("./public/html/auth/register.html")
|
||||||
pub const PROFILE_BASE: &str = include_str!("./public/html/profile/base.html");
|
pub const PROFILE_BASE: &str = include_str!("./public/html/profile/base.html");
|
||||||
pub const PROFILE_POSTS: &str = include_str!("./public/html/profile/posts.html");
|
pub const PROFILE_POSTS: &str = include_str!("./public/html/profile/posts.html");
|
||||||
pub const PROFILE_SETTINGS: &str = include_str!("./public/html/profile/settings.html");
|
pub const PROFILE_SETTINGS: &str = include_str!("./public/html/profile/settings.html");
|
||||||
|
pub const PROFILE_FOLLOWING: &str = include_str!("./public/html/profile/following.html");
|
||||||
|
pub const PROFILE_FOLLOWERS: &str = include_str!("./public/html/profile/followers.html");
|
||||||
|
|
||||||
pub const COMMUNITIES_LIST: &str = include_str!("./public/html/communities/list.html");
|
pub const COMMUNITIES_LIST: &str = include_str!("./public/html/communities/list.html");
|
||||||
pub const COMMUNITIES_BASE: &str = include_str!("./public/html/communities/base.html");
|
pub const COMMUNITIES_BASE: &str = include_str!("./public/html/communities/base.html");
|
||||||
|
@ -159,6 +161,8 @@ pub(crate) async fn write_assets(config: &Config) -> PathBufD {
|
||||||
write_template!(html_path->"profile/base.html"(crate::assets::PROFILE_BASE) -d "profile" --config=config);
|
write_template!(html_path->"profile/base.html"(crate::assets::PROFILE_BASE) -d "profile" --config=config);
|
||||||
write_template!(html_path->"profile/posts.html"(crate::assets::PROFILE_POSTS) --config=config);
|
write_template!(html_path->"profile/posts.html"(crate::assets::PROFILE_POSTS) --config=config);
|
||||||
write_template!(html_path->"profile/settings.html"(crate::assets::PROFILE_SETTINGS) --config=config);
|
write_template!(html_path->"profile/settings.html"(crate::assets::PROFILE_SETTINGS) --config=config);
|
||||||
|
write_template!(html_path->"profile/following.html"(crate::assets::PROFILE_FOLLOWING) --config=config);
|
||||||
|
write_template!(html_path->"profile/followers.html"(crate::assets::PROFILE_FOLLOWERS) --config=config);
|
||||||
|
|
||||||
write_template!(html_path->"communities/list.html"(crate::assets::COMMUNITIES_LIST) -d "communities" --config=config);
|
write_template!(html_path->"communities/list.html"(crate::assets::COMMUNITIES_LIST) -d "communities" --config=config);
|
||||||
write_template!(html_path->"communities/base.html"(crate::assets::COMMUNITIES_BASE) --config=config);
|
write_template!(html_path->"communities/base.html"(crate::assets::COMMUNITIES_BASE) --config=config);
|
||||||
|
|
|
@ -226,6 +226,10 @@ a {
|
||||||
color: var(--color-link);
|
color: var(--color-link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.flush {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
@ -633,6 +637,7 @@ nav .button:not(.inner *) {
|
||||||
transition:
|
transition:
|
||||||
opacity 0.15s,
|
opacity 0.15s,
|
||||||
transform 0.15s;
|
transform 0.15s;
|
||||||
|
font-size: 0.95rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav button:not(.inner *):hover,
|
nav button:not(.inner *):hover,
|
||||||
|
|
|
@ -87,17 +87,31 @@ community %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</button>
|
</button>
|
||||||
{%- endmacro %} {% macro post(post, owner, secondary=false, community=false,
|
{%- endmacro %} {% macro post(post, owner, secondary=false, community=false,
|
||||||
show_community=true) -%}
|
show_community=true) -%} {% if community and show_community %}
|
||||||
<div class="card flex flex-col gap-2 {% if secondary %}secondary{% endif %}">
|
<div class="card-nest">
|
||||||
|
<div class="card small">
|
||||||
|
<a
|
||||||
|
href="/api/v1/communities/find/{{ post.community }}"
|
||||||
|
class="flush flex gap-1 items-center"
|
||||||
|
>
|
||||||
|
{{ components::community_avatar(id=post.community,
|
||||||
|
community=community) }}
|
||||||
|
<b>{{ community.title }}</b>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div
|
||||||
|
class="card flex flex-col gap-2 {% if secondary %}secondary{% endif %}"
|
||||||
|
>
|
||||||
<div class="w-full flex gap-2">
|
<div class="w-full flex gap-2">
|
||||||
<a href="/user/{{ owner.username }}">
|
<a href="/@{{ owner.username }}">
|
||||||
{{ components::avatar(username=post.owner, size="52px",
|
{{ components::avatar(username=post.owner, size="52px",
|
||||||
selector_type="id") }}
|
selector_type="id") }}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="flex flex-col w-full gap-1">
|
<div class="flex flex-col w-full gap-1">
|
||||||
<div class="flex flex-wrap gap-2 items-center">
|
<div class="flex flex-wrap gap-2 items-center">
|
||||||
<a href="/user/{{ owner.username }}"
|
<a href="/@{{ owner.username }}"
|
||||||
>{{ components::username(user=owner) }}</a
|
>{{ components::username(user=owner) }}</a
|
||||||
>
|
>
|
||||||
|
|
||||||
|
@ -106,10 +120,7 @@ show_community=true) -%}
|
||||||
{% if show_community %}
|
{% if show_community %}
|
||||||
<a href="/api/v1/communities/find/{{ post.community }}">
|
<a href="/api/v1/communities/find/{{ post.community }}">
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
{% if community %}
|
{% if not community %}
|
||||||
{{ components::community_avatar(id=post.community,
|
|
||||||
community=community) }}
|
|
||||||
{% else %}
|
|
||||||
{{ components::community_avatar(id=post.community) }}
|
{{ components::community_avatar(id=post.community) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -174,7 +185,9 @@ show_community=true) -%}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%- endmacro %} {% macro notification(notification) -%}
|
{% if community and show_community %}
|
||||||
|
</div>
|
||||||
|
{% endif %} {%- endmacro %} {% macro notification(notification) -%}
|
||||||
<div class="w-full card-nest">
|
<div class="w-full card-nest">
|
||||||
<div class="card small notif_title flex items-center">
|
<div class="card small notif_title flex items-center">
|
||||||
{% if not notification.read %}
|
{% if not notification.read %}
|
||||||
|
@ -222,4 +235,15 @@ show_community=true) -%}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{%- endmacro %} {% macro user_card(user) -%}
|
||||||
|
<a
|
||||||
|
class="card secondary w-full flex items-center gap-4"
|
||||||
|
href="/@{{ user.username }}"
|
||||||
|
>
|
||||||
|
{{ components::avatar(username=user.username, size="48px") }}
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<h3>{{ components::username(user=user) }}</h3>
|
||||||
|
<span class="fade">{{ user.username }}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
|
@ -65,7 +65,7 @@ show_lhs=true) -%}
|
||||||
|
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<b class="title">{{ user.username }}</b>
|
<b class="title">{{ user.username }}</b>
|
||||||
<a href="/user/{{ user.username }}">
|
<a href="/@{{ user.username }}">
|
||||||
{{ icon "circle-user-round" }}
|
{{ icon "circle-user-round" }}
|
||||||
<span>{{ text "auth:link.my_profile" }}</span>
|
<span>{{ text "auth:link.my_profile" }}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -34,14 +34,14 @@
|
||||||
<div class="card flex flex-col gap-2" id="social">
|
<div class="card flex flex-col gap-2" id="social">
|
||||||
<div class="w-full flex">
|
<div class="w-full flex">
|
||||||
<a
|
<a
|
||||||
href="/user/{{ profile.username }}/followers"
|
href="/@{{ profile.username }}/followers"
|
||||||
class="w-full flex justify-center items-center gap-2"
|
class="w-full flex justify-center items-center gap-2"
|
||||||
>
|
>
|
||||||
<h4>{{ profile.follower_count }}</h4>
|
<h4>{{ profile.follower_count }}</h4>
|
||||||
<span>{{ text "auth:label.followers" }}</span>
|
<span>{{ text "auth:label.followers" }}</span>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="/user/{{ profile.username }}/following"
|
href="/@{{ profile.username }}/following"
|
||||||
class="w-full flex justify-center items-center gap-2"
|
class="w-full flex justify-center items-center gap-2"
|
||||||
>
|
>
|
||||||
<h4>{{ profile.following_count }}</h4>
|
<h4>{{ profile.following_count }}</h4>
|
||||||
|
|
22
crates/app/src/public/html/profile/followers.html
Normal file
22
crates/app/src/public/html/profile/followers.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{% import "macros.html" as macros %} {% extends "profile/base.html" %} {% block
|
||||||
|
content %}
|
||||||
|
<div class="card-nest">
|
||||||
|
<div class="card small flex gap-2 items-center">
|
||||||
|
{{ icon "users-round" }}
|
||||||
|
<span>{{ text "auth:label.followers" }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card flex flex-col gap-4">
|
||||||
|
<!-- prettier-ignore -->
|
||||||
|
{% for item in list %}
|
||||||
|
<div class="card-nest">
|
||||||
|
<div class="card small">
|
||||||
|
Since <span class="date">{{ item[0].created }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ components::user_card(user=item[1]) }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
22
crates/app/src/public/html/profile/following.html
Normal file
22
crates/app/src/public/html/profile/following.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{% import "macros.html" as macros %} {% extends "profile/base.html" %} {% block
|
||||||
|
content %}
|
||||||
|
<div class="card-nest">
|
||||||
|
<div class="card small flex gap-2 items-center">
|
||||||
|
{{ icon "users-round" }}
|
||||||
|
<span>{{ text "auth:label.following" }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card flex flex-col gap-4">
|
||||||
|
<!-- prettier-ignore -->
|
||||||
|
{% for item in list %}
|
||||||
|
<div class="card-nest">
|
||||||
|
<div class="card small">
|
||||||
|
Since <span class="date">{{ item[0].created }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ components::user_card(user=item[1]) }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -18,14 +18,15 @@ pub async fn redirect_from_id(
|
||||||
Extension(data): Extension<State>,
|
Extension(data): Extension<State>,
|
||||||
Path(id): Path<String>,
|
Path(id): Path<String>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
match (data.read().await).0
|
match (data.read().await)
|
||||||
|
.0
|
||||||
.get_user_by_id(match id.parse::<usize>() {
|
.get_user_by_id(match id.parse::<usize>() {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
Err(_) => return Redirect::to("/"),
|
Err(_) => return Redirect::to("/"),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(u) => Redirect::to(&format!("/user/{}", u.username)),
|
Ok(u) => Redirect::to(&format!("/@{}", u.username)),
|
||||||
Err(_) => Redirect::to("/"),
|
Err(_) => Redirect::to("/"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,9 @@ pub fn routes() -> Router {
|
||||||
.route("/auth/login", get(auth::login_request))
|
.route("/auth/login", get(auth::login_request))
|
||||||
// profile
|
// profile
|
||||||
.route("/settings", get(profile::settings_request))
|
.route("/settings", get(profile::settings_request))
|
||||||
.route("/user/{username}", get(profile::posts_request))
|
.route("/@{username}", get(profile::posts_request))
|
||||||
|
.route("/@{username}/following", get(profile::following_request))
|
||||||
|
.route("/@{username}/followers", get(profile::followers_request))
|
||||||
// communities
|
// communities
|
||||||
.route("/communities", get(communities::list_request))
|
.route("/communities", get(communities::list_request))
|
||||||
.route("/community/{title}", get(communities::feed_request))
|
.route("/community/{title}", get(communities::feed_request))
|
||||||
|
|
|
@ -66,7 +66,7 @@ pub fn profile_context(
|
||||||
context.insert("is_blocking", &is_blocking);
|
context.insert("is_blocking", &is_blocking);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `/user/{username}`
|
/// `/@{username}`
|
||||||
pub async fn posts_request(
|
pub async fn posts_request(
|
||||||
jar: CookieJar,
|
jar: CookieJar,
|
||||||
Path(username): Path<String>,
|
Path(username): Path<String>,
|
||||||
|
@ -189,3 +189,255 @@ pub async fn posts_request(
|
||||||
// return
|
// return
|
||||||
Ok(Html(data.1.render("profile/posts.html", &context).unwrap()))
|
Ok(Html(data.1.render("profile/posts.html", &context).unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `/@{username}/following`
|
||||||
|
pub async fn following_request(
|
||||||
|
jar: CookieJar,
|
||||||
|
Path(username): Path<String>,
|
||||||
|
Query(props): Query<PaginatedQuery>,
|
||||||
|
Extension(data): Extension<State>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let data = data.read().await;
|
||||||
|
let user = get_user_from_token!(jar, data.0);
|
||||||
|
|
||||||
|
let other_user = match data.0.get_user_by_username(&username).await {
|
||||||
|
Ok(ua) => ua,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// check if we're blocked
|
||||||
|
if let Some(ref ua) = user {
|
||||||
|
if data
|
||||||
|
.0
|
||||||
|
.get_userblock_by_initiator_receiver(other_user.id, ua.id)
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
return Err(Html(
|
||||||
|
render_error(Error::NotAllowed, &jar, &data, &user).await,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for private profile
|
||||||
|
if other_user.settings.private_profile {
|
||||||
|
if let Some(ref ua) = user {
|
||||||
|
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(
|
||||||
|
render_error(Error::NotAllowed, &jar, &data, &user).await,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch data
|
||||||
|
let list = match data
|
||||||
|
.0
|
||||||
|
.get_userfollows_by_initiator(other_user.id, 12, props.page)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(l) => match data.0.fill_userfollows_with_receiver(l).await {
|
||||||
|
Ok(l) => l,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
|
},
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let communities = match data.0.get_memberships_by_owner(other_user.id).await {
|
||||||
|
Ok(m) => match data.0.fill_communities(m).await {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
|
},
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// init context
|
||||||
|
let lang = get_lang!(jar, data.0);
|
||||||
|
let mut context = initial_context(&data.0.0, lang, &user).await;
|
||||||
|
|
||||||
|
let is_self = if let Some(ref ua) = user {
|
||||||
|
ua.id == other_user.id
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_following = if let Some(ref ua) = user {
|
||||||
|
data.0
|
||||||
|
.get_userfollow_by_initiator_receiver(ua.id, other_user.id)
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
} else {
|
||||||
|
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("list", &list);
|
||||||
|
profile_context(
|
||||||
|
&mut context,
|
||||||
|
&other_user,
|
||||||
|
&communities,
|
||||||
|
is_self,
|
||||||
|
is_following,
|
||||||
|
is_following_you,
|
||||||
|
is_blocking,
|
||||||
|
);
|
||||||
|
|
||||||
|
// return
|
||||||
|
Ok(Html(
|
||||||
|
data.1.render("profile/following.html", &context).unwrap(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `/@{username}/followers`
|
||||||
|
pub async fn followers_request(
|
||||||
|
jar: CookieJar,
|
||||||
|
Path(username): Path<String>,
|
||||||
|
Query(props): Query<PaginatedQuery>,
|
||||||
|
Extension(data): Extension<State>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let data = data.read().await;
|
||||||
|
let user = get_user_from_token!(jar, data.0);
|
||||||
|
|
||||||
|
let other_user = match data.0.get_user_by_username(&username).await {
|
||||||
|
Ok(ua) => ua,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// check if we're blocked
|
||||||
|
if let Some(ref ua) = user {
|
||||||
|
if data
|
||||||
|
.0
|
||||||
|
.get_userblock_by_initiator_receiver(other_user.id, ua.id)
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
return Err(Html(
|
||||||
|
render_error(Error::NotAllowed, &jar, &data, &user).await,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for private profile
|
||||||
|
if other_user.settings.private_profile {
|
||||||
|
if let Some(ref ua) = user {
|
||||||
|
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(
|
||||||
|
render_error(Error::NotAllowed, &jar, &data, &user).await,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch data
|
||||||
|
let list = match data
|
||||||
|
.0
|
||||||
|
.get_userfollows_by_receiver(other_user.id, 12, props.page)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(l) => match data.0.fill_userfollows_with_initiator(l).await {
|
||||||
|
Ok(l) => l,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
|
},
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let communities = match data.0.get_memberships_by_owner(other_user.id).await {
|
||||||
|
Ok(m) => match data.0.fill_communities(m).await {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
|
},
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// init context
|
||||||
|
let lang = get_lang!(jar, data.0);
|
||||||
|
let mut context = initial_context(&data.0.0, lang, &user).await;
|
||||||
|
|
||||||
|
let is_self = if let Some(ref ua) = user {
|
||||||
|
ua.id == other_user.id
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_following = if let Some(ref ua) = user {
|
||||||
|
data.0
|
||||||
|
.get_userfollow_by_initiator_receiver(ua.id, other_user.id)
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
} else {
|
||||||
|
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("list", &list);
|
||||||
|
profile_context(
|
||||||
|
&mut context,
|
||||||
|
&other_user,
|
||||||
|
&communities,
|
||||||
|
is_self,
|
||||||
|
is_following,
|
||||||
|
is_following_you,
|
||||||
|
is_blocking,
|
||||||
|
);
|
||||||
|
|
||||||
|
// return
|
||||||
|
Ok(Html(
|
||||||
|
data.1.render("profile/followers.html", &context).unwrap(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
|
@ -137,6 +137,9 @@ pub struct Config {
|
||||||
/// version built with the server binary.
|
/// version built with the server binary.
|
||||||
#[serde(default = "default_no_track")]
|
#[serde(default = "default_no_track")]
|
||||||
pub no_track: Vec<String>,
|
pub no_track: Vec<String>,
|
||||||
|
/// A list of usernames which cannot be used.
|
||||||
|
#[serde(default = "default_banned_usernames")]
|
||||||
|
pub banned_usernames: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_name() -> String {
|
fn default_name() -> String {
|
||||||
|
@ -170,6 +173,19 @@ fn default_no_track() -> Vec<String> {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_banned_usernames() -> Vec<String> {
|
||||||
|
vec![
|
||||||
|
"admin".to_string(),
|
||||||
|
"owner".to_string(),
|
||||||
|
"moderator".to_string(),
|
||||||
|
"api".to_string(),
|
||||||
|
"communities".to_string(),
|
||||||
|
"notifs".to_string(),
|
||||||
|
"notification".to_string(),
|
||||||
|
"post".to_string(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -181,6 +197,7 @@ impl Default for Config {
|
||||||
security: default_security(),
|
security: default_security(),
|
||||||
dirs: default_dirs(),
|
dirs: default_dirs(),
|
||||||
no_track: default_no_track(),
|
no_track: default_no_track(),
|
||||||
|
banned_usernames: default_banned_usernames(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,10 @@ impl DataManager {
|
||||||
return Err(Error::DataTooShort("password".to_string()));
|
return Err(Error::DataTooShort("password".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.0.banned_usernames.contains(&data.username) {
|
||||||
|
return Err(Error::MiscError("This username cannot be used".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
// make sure username isn't taken
|
// make sure username isn't taken
|
||||||
if self.get_user_by_username(&data.username).await.is_ok() {
|
if self.get_user_by_username(&data.username).await.is_ok() {
|
||||||
return Err(Error::UsernameInUse);
|
return Err(Error::UsernameInUse);
|
||||||
|
|
|
@ -145,6 +145,36 @@ impl DataManager {
|
||||||
Ok(res.unwrap())
|
Ok(res.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Complete a vector of just userfollows with their receiver as well.
|
||||||
|
pub async fn fill_userfollows_with_receiver(
|
||||||
|
&self,
|
||||||
|
userfollows: Vec<UserFollow>,
|
||||||
|
) -> Result<Vec<(UserFollow, User)>> {
|
||||||
|
let mut out: Vec<(UserFollow, User)> = Vec::new();
|
||||||
|
|
||||||
|
for userfollow in userfollows {
|
||||||
|
let receiver = userfollow.receiver.clone();
|
||||||
|
out.push((userfollow, self.get_user_by_id(receiver).await?));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Complete a vector of just userfollows with their initiator as well.
|
||||||
|
pub async fn fill_userfollows_with_initiator(
|
||||||
|
&self,
|
||||||
|
userfollows: Vec<UserFollow>,
|
||||||
|
) -> Result<Vec<(UserFollow, User)>> {
|
||||||
|
let mut out: Vec<(UserFollow, User)> = Vec::new();
|
||||||
|
|
||||||
|
for userfollow in userfollows {
|
||||||
|
let initiator = userfollow.initiator.clone();
|
||||||
|
out.push((userfollow, self.get_user_by_id(initiator).await?));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new user follow in the database.
|
/// Create a new user follow in the database.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue