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_POSTS: &str = include_str!("./public/html/profile/posts.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_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/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/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/base.html"(crate::assets::COMMUNITIES_BASE) --config=config);
|
||||
|
|
|
@ -226,6 +226,10 @@ a {
|
|||
color: var(--color-link);
|
||||
}
|
||||
|
||||
a.flush {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
@ -633,6 +637,7 @@ nav .button:not(.inner *) {
|
|||
transition:
|
||||
opacity 0.15s,
|
||||
transform 0.15s;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
nav button:not(.inner *):hover,
|
||||
|
|
|
@ -87,94 +87,107 @@ community %}
|
|||
{% endif %}
|
||||
</button>
|
||||
{%- endmacro %} {% macro post(post, owner, secondary=false, community=false,
|
||||
show_community=true) -%}
|
||||
<div class="card flex flex-col gap-2 {% if secondary %}secondary{% endif %}">
|
||||
<div class="w-full flex gap-2">
|
||||
<a href="/user/{{ owner.username }}">
|
||||
{{ components::avatar(username=post.owner, size="52px",
|
||||
selector_type="id") }}
|
||||
</a>
|
||||
|
||||
<div class="flex flex-col w-full gap-1">
|
||||
<div class="flex flex-wrap gap-2 items-center">
|
||||
<a href="/user/{{ owner.username }}"
|
||||
>{{ components::username(user=owner) }}</a
|
||||
>
|
||||
|
||||
<span class="fade date">{{ post.created }}</span>
|
||||
|
||||
{% if show_community %}
|
||||
<a href="/api/v1/communities/find/{{ post.community }}">
|
||||
<!-- prettier-ignore -->
|
||||
{% if community %}
|
||||
{{ components::community_avatar(id=post.community,
|
||||
community=community) }}
|
||||
{% else %}
|
||||
{{ components::community_avatar(id=post.community) }}
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<span id="post-content:{{ post.id }}"
|
||||
>{{ post.content|markdown|safe }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center gap-2 w-full">
|
||||
{% if user %}
|
||||
<div
|
||||
class="flex gap-1 reactions_box"
|
||||
hook="check_reactions"
|
||||
hook-arg:id="{{ post.id }}"
|
||||
show_community=true) -%} {% if community and show_community %}
|
||||
<div class="card-nest">
|
||||
<div class="card small">
|
||||
<a
|
||||
href="/api/v1/communities/find/{{ post.community }}"
|
||||
class="flush flex gap-1 items-center"
|
||||
>
|
||||
{{ components::likes(id=post.id, asset_type="Post",
|
||||
likes=post.likes, dislikes=post.dislikes) }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div></div>
|
||||
{% endif %}
|
||||
|
||||
<div class="flex gap-1 buttons_box">
|
||||
<a href="/post/{{ post.id }}" class="button camo small">
|
||||
{{ icon "message-circle" }}
|
||||
<span>{{ post.comment_count }}</span>
|
||||
{{ 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">
|
||||
<a href="/@{{ owner.username }}">
|
||||
{{ components::avatar(username=post.owner, size="52px",
|
||||
selector_type="id") }}
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="/post/{{ post.id }}"
|
||||
class="button camo small"
|
||||
target="_blank"
|
||||
>
|
||||
{{ icon "external-link" }}
|
||||
</a>
|
||||
|
||||
{% if user %} {% if (user.id == post.owner) or is_helper %}
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="camo small"
|
||||
onclick="trigger('atto::hooks::dropdown', [event])"
|
||||
exclude="dropdown"
|
||||
>
|
||||
{{ icon "ellipsis" }}
|
||||
</button>
|
||||
|
||||
<div class="inner">
|
||||
<button
|
||||
class="red"
|
||||
onclick="trigger('me::remove_post', ['{{ post.id }}'])"
|
||||
<div class="flex flex-col w-full gap-1">
|
||||
<div class="flex flex-wrap gap-2 items-center">
|
||||
<a href="/@{{ owner.username }}"
|
||||
>{{ components::username(user=owner) }}</a
|
||||
>
|
||||
{{ icon "trash" }}
|
||||
<span>{{ text "general:action.delete" }}</span>
|
||||
</button>
|
||||
|
||||
<span class="fade date">{{ post.created }}</span>
|
||||
|
||||
{% if show_community %}
|
||||
<a href="/api/v1/communities/find/{{ post.community }}">
|
||||
<!-- prettier-ignore -->
|
||||
{% if not community %}
|
||||
{{ components::community_avatar(id=post.community) }}
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<span id="post-content:{{ post.id }}"
|
||||
>{{ post.content|markdown|safe }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center gap-2 w-full">
|
||||
{% if user %}
|
||||
<div
|
||||
class="flex gap-1 reactions_box"
|
||||
hook="check_reactions"
|
||||
hook-arg:id="{{ post.id }}"
|
||||
>
|
||||
{{ components::likes(id=post.id, asset_type="Post",
|
||||
likes=post.likes, dislikes=post.dislikes) }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div></div>
|
||||
{% endif %}
|
||||
|
||||
<div class="flex gap-1 buttons_box">
|
||||
<a href="/post/{{ post.id }}" class="button camo small">
|
||||
{{ icon "message-circle" }}
|
||||
<span>{{ post.comment_count }}</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="/post/{{ post.id }}"
|
||||
class="button camo small"
|
||||
target="_blank"
|
||||
>
|
||||
{{ icon "external-link" }}
|
||||
</a>
|
||||
|
||||
{% if user %} {% if (user.id == post.owner) or is_helper %}
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="camo small"
|
||||
onclick="trigger('atto::hooks::dropdown', [event])"
|
||||
exclude="dropdown"
|
||||
>
|
||||
{{ icon "ellipsis" }}
|
||||
</button>
|
||||
|
||||
<div class="inner">
|
||||
<button
|
||||
class="red"
|
||||
onclick="trigger('me::remove_post', ['{{ post.id }}'])"
|
||||
>
|
||||
{{ icon "trash" }}
|
||||
<span>{{ text "general:action.delete" }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %} {% endif %}
|
||||
</div>
|
||||
{% endif %} {% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if community and show_community %}
|
||||
</div>
|
||||
{%- endmacro %} {% macro notification(notification) -%}
|
||||
{% endif %} {%- endmacro %} {% macro notification(notification) -%}
|
||||
<div class="w-full card-nest">
|
||||
<div class="card small notif_title flex items-center">
|
||||
{% if not notification.read %}
|
||||
|
@ -222,4 +235,15 @@ show_community=true) -%}
|
|||
</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 %}
|
||||
|
|
|
@ -65,7 +65,7 @@ show_lhs=true) -%}
|
|||
|
||||
<div class="inner">
|
||||
<b class="title">{{ user.username }}</b>
|
||||
<a href="/user/{{ user.username }}">
|
||||
<a href="/@{{ user.username }}">
|
||||
{{ icon "circle-user-round" }}
|
||||
<span>{{ text "auth:link.my_profile" }}</span>
|
||||
</a>
|
||||
|
|
|
@ -34,14 +34,14 @@
|
|||
<div class="card flex flex-col gap-2" id="social">
|
||||
<div class="w-full flex">
|
||||
<a
|
||||
href="/user/{{ profile.username }}/followers"
|
||||
href="/@{{ profile.username }}/followers"
|
||||
class="w-full flex justify-center items-center gap-2"
|
||||
>
|
||||
<h4>{{ profile.follower_count }}</h4>
|
||||
<span>{{ text "auth:label.followers" }}</span>
|
||||
</a>
|
||||
<a
|
||||
href="/user/{{ profile.username }}/following"
|
||||
href="/@{{ profile.username }}/following"
|
||||
class="w-full flex justify-center items-center gap-2"
|
||||
>
|
||||
<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>,
|
||||
Path(id): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
match (data.read().await).0
|
||||
match (data.read().await)
|
||||
.0
|
||||
.get_user_by_id(match id.parse::<usize>() {
|
||||
Ok(id) => id,
|
||||
Err(_) => return Redirect::to("/"),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(u) => Redirect::to(&format!("/user/{}", u.username)),
|
||||
Ok(u) => Redirect::to(&format!("/@{}", u.username)),
|
||||
Err(_) => Redirect::to("/"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,9 @@ pub fn routes() -> Router {
|
|||
.route("/auth/login", get(auth::login_request))
|
||||
// profile
|
||||
.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
|
||||
.route("/communities", get(communities::list_request))
|
||||
.route("/community/{title}", get(communities::feed_request))
|
||||
|
|
|
@ -66,7 +66,7 @@ pub fn profile_context(
|
|||
context.insert("is_blocking", &is_blocking);
|
||||
}
|
||||
|
||||
/// `/user/{username}`
|
||||
/// `/@{username}`
|
||||
pub async fn posts_request(
|
||||
jar: CookieJar,
|
||||
Path(username): Path<String>,
|
||||
|
@ -189,3 +189,255 @@ pub async fn posts_request(
|
|||
// return
|
||||
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.
|
||||
#[serde(default = "default_no_track")]
|
||||
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 {
|
||||
|
@ -170,6 +173,19 @@ fn default_no_track() -> Vec<String> {
|
|||
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 {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
@ -181,6 +197,7 @@ impl Default for Config {
|
|||
security: default_security(),
|
||||
dirs: default_dirs(),
|
||||
no_track: default_no_track(),
|
||||
banned_usernames: default_banned_usernames(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,10 @@ impl DataManager {
|
|||
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
|
||||
if self.get_user_by_username(&data.username).await.is_ok() {
|
||||
return Err(Error::UsernameInUse);
|
||||
|
|
|
@ -145,6 +145,36 @@ impl DataManager {
|
|||
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.
|
||||
///
|
||||
/// # Arguments
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue