tetratto/crates/app/src/public/html/profile/base.html

394 lines
18 KiB
HTML
Raw Normal View History

{% import "macros.html" as macros %} {% extends "root.html" %} {% block head %}
<title>{{ profile.username }} - {{ config.name }}</title>
{% endblock %} {% block body %} {{ macros::nav() }}
<article>
2025-03-31 19:31:36 -04:00
<div class="content_container flex flex-col gap-4">
{{ components::banner(username=profile.username) }}
<div class="w-full flex gap-4 flex-collapse">
<div
class="lhs flex flex-col gap-2 sm:w-full"
style="min-width: 20rem"
>
<div class="card-nest w-full">
<div class="card flex gap-2" id="user_avatar_and_name">
{{
components::avatar(username=profile.username,size="72px")
}}
<div class="flex flex-col">
<!-- prettier-ignore -->
<h3 id="username" class="username flex items-center gap-2">
2025-03-31 11:45:34 -04:00
{{ components::username(user=profile) }}
{% if profile.is_verified %}
<span title="Verified" style="color: var(--color-primary);" class="flex items-center">
{{ icon "badge-check" }}
</span>
{% endif %}
</h3>
<span class="fade">{{ profile.username }}</span>
</div>
</div>
<div class="card flex flex-col gap-2" id="social">
<div class="w-full flex">
<a
2025-03-31 22:35:11 -04:00
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
2025-03-31 22:35:11 -04:00
href="/@{{ profile.username }}/following"
class="w-full flex justify-center items-center gap-2"
>
<h4>{{ profile.following_count }}</h4>
<span>{{ text "auth:label.following" }}</span>
</a>
</div>
</div>
</div>
<div class="card-nest flex flex-col">
<div id="bio" class="card small">
{{ profile.settings.biography|markdown|safe }}
</div>
<div class="card flex flex-col gap-2">
<div class="w-full flex justify-between items-center">
<span class="notification chip">ID</span>
<button
title="Copy"
onclick="trigger('atto::copy_text', ['{{ profile.id }}'])"
class="camo small"
>
{{ icon "copy" }}
</button>
</div>
<div class="w-full flex justify-between items-center">
<span class="notification chip">Joined</span>
<span class="date">{{ profile.created }}</span>
</div>
2025-04-02 14:11:01 -04:00
{% if not profile.settings.private_last_seen or is_self
or is_helper %}
<div class="w-full flex justify-between items-center">
<span class="notification chip">Last seen</span>
<span class="date">{{ profile.last_seen }}</span>
</div>
{% endif %}
</div>
</div>
{% if not is_self and user %}
<div class="card-nest">
<div class="card small">
<b>{{ text "auth:label.relationship" }}</b>
</div>
<div class="card flex gap-2 flex-wrap">
{% if not is_blocking %} {% if not is_following %}
<button onclick="toggle_follow_user()">
{{ icon "user-plus" }}
<span>{{ text "auto:action.follow" }}</span>
</button>
{% else %}
<button
onclick="toggle_follow_user()"
class="quaternary red"
>
{{ icon "user-minus" }}
<span>{{ text "auto:action.unfollow" }}</span>
</button>
{% endif %}
<button
onclick="toggle_block_user()"
class="quaternary red"
>
{{ icon "shield" }}
<span>{{ text "auto:action.block" }}</span>
</button>
{% else %}
<button
onclick="toggle_block_user()"
class="quaternary red"
>
{{ icon "shield-off" }}
<span>{{ text "auto:action.unblock" }}</span>
</button>
{% endif %}
<script>
globalThis.toggle_follow_user = () => {
fetch(
"/api/v1/auth/profile/{{ profile.id }}/follow",
{
method: "POST",
},
)
.then((res) => res.json())
.then((res) => {
trigger("atto::toast", [
res.ok ? "success" : "error",
res.message,
]);
});
};
globalThis.toggle_block_user = async () => {
if (
!(await trigger("atto::confirm", [
"Are you sure you would like to do this?",
]))
) {
return;
}
fetch(
"/api/v1/auth/profile/{{ profile.id }}/block",
{
method: "POST",
},
)
.then((res) => res.json())
.then((res) => {
trigger("atto::toast", [
res.ok ? "success" : "error",
res.message,
]);
});
};
</script>
</div>
</div>
{% endif %} {% if not profile.settings.private_communities or
is_self or is_helper %}
<div class="card-nest">
<div class="card small flex gap-2 items-center">
{{ icon "users-round" }}
<span>{{ text "auth:label.joined_communities" }}</span>
</div>
<div class="card flex flex-wrap gap-2">
{% for community in communities %}
<a href="/community/{{ community.title }}">
{{ components::community_avatar(id=community.id,
community=community, size="48px") }}
</a>
{% endfor %}
</div>
</div>
{% endif %}
</div>
<div class="rhs w-full flex flex-col gap-4">
{% if is_helper %}
<div class="card-nest">
<div class="card small flex items-center gap-2">
{{ icon "shield" }}
<span>{{ text "auth:label.moderation" }}</span>
</div>
<div class="card tertiary">
<div class="flex flex-col gap-2" id="mod_options">
<div
class="card w-full flex flex-wrap gap-2"
ui_ident="actions"
>
<a
href="/settings?username={{ profile.username }}"
class="button quaternary"
>
{{ icon "settings" }}
<span>View settings</span>
</a>
<button
class="red quaternary"
onclick="delete_account(event)"
>
{{ icon "trash" }}
<span
>{{ text "settings:label.delete_account"
}}</span
>
</button>
{% if profile.permissions != 131073 %}
<button
class="red quaternary"
onclick="update_user_role(131073)"
>
Ban
</button>
{% else %}
<button
class="quaternary"
onclick="update_user_role(1)"
>
Unban
</button>
{% endif %}
</div>
</div>
<script>
setTimeout(() => {
const ui = ns("ui");
const element =
document.getElementById("mod_options");
async function profile_request(
do_confirm,
path,
body,
) {
if (do_confirm) {
if (
!(await trigger("atto::confirm", [
"Are you sure you would like to do this?",
]))
) {
return;
}
}
fetch(
`/api/v1/auth/profile/{{ profile.id }}/${path}`,
{
method: "POST",
headers: {
"Content-Type":
"application/json",
},
body: JSON.stringify(body),
},
)
.then((res) => res.json())
.then((res) => {
trigger("atto::toast", [
res.ok ? "success" : "error",
res.message,
]);
});
}
globalThis.delete_account = async (e) => {
e.preventDefault();
if (
!(await trigger("atto::confirm", [
"Are you sure you would like to do this?",
]))
) {
return;
}
fetch(
"/api/v1/auth/profile/{{ profile.id }}",
{
method: "DELETE",
headers: {
"Content-Type":
"application/json",
},
body: JSON.stringify({
password: "",
}),
},
)
.then((res) => res.json())
.then((res) => {
trigger("atto::toast", [
res.ok ? "success" : "error",
res.message,
]);
});
};
globalThis.update_user_role = async (
new_role,
) => {
if (
!(await trigger("atto::confirm", [
"Are you sure you would like to do this?",
]))
) {
return;
}
fetch(
`/api/v1/auth/profile/{{ profile.id }}/role`,
{
method: "POST",
headers: {
"Content-Type":
"application/json",
},
body: JSON.stringify({
role: Number.parseInt(new_role),
}),
},
)
.then((res) => res.json())
.then((res) => {
trigger("atto::toast", [
res.ok ? "success" : "error",
res.message,
]);
});
};
ui.refresh_container(element, ["actions"]);
setTimeout(() => {
ui.refresh_container(element, ["actions"]);
ui.generate_settings_ui(
element,
[
[
["is_verified", "Is verified"],
"{{ profile.is_verified }}",
"checkbox",
],
[
["role", "Permission level"],
"{{ profile.permissions }}",
"input",
],
],
null,
{
is_verified: (value) => {
profile_request(
false,
"verified",
{
is_verified: value,
},
);
},
role: (new_role) => {
return update_user_role(
new_role,
);
},
},
);
}, 100);
}, 150);
</script>
</div>
</div>
{% endif %} {% block content %}{% endblock %}
</div>
</div>
</div>
</article>
{% endblock %}