add: finish ui rewrite
This commit is contained in:
parent
e9846016e6
commit
5dec98d698
119 changed files with 8776 additions and 9350 deletions
|
@ -1,27 +0,0 @@
|
|||
{% extends "root.html" %} {% block head %}
|
||||
<title>{{ profile.username }} (banned) - {{ config.name }}</title>
|
||||
{% endblock %} {% block body %} {{ macros::nav() }}
|
||||
<main class="flex flex-col gap-2">
|
||||
<div class="card-nest">
|
||||
<div class="card small flex items-center justify-between gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
{{ components::avatar(username=profile.username, size="24px") }}
|
||||
<span>{{ profile.username }}</span>
|
||||
</div>
|
||||
|
||||
<b class="notification chip">{{ text "auth:label.banned" }}</b>
|
||||
</div>
|
||||
|
||||
<div class="card flex flex-col gap-2">
|
||||
<span>{{ text "auth:label.banned_message" }}</span>
|
||||
|
||||
<div class="card w-full secondary flex gap-2">
|
||||
<a href="/" class="button red quaternary">
|
||||
{{ icon "x" }}
|
||||
<span>{{ text "general:action.back" }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
33
crates/app/src/public/html/profile/banned.lisp
Normal file
33
crates/app/src/public/html/profile/banned.lisp
Normal file
|
@ -0,0 +1,33 @@
|
|||
(text "{% extends \"root.html\" %} {% block head %}")
|
||||
(title
|
||||
(text "{{ profile.username }} (banned) - {{ config.name }}"))
|
||||
|
||||
(text "{% endblock %} {% block body %} {{ macros::nav() }}")
|
||||
(main
|
||||
("class" "flex flex-col gap-2")
|
||||
(div
|
||||
("class" "card-nest")
|
||||
(div
|
||||
("class" "card small flex items-center justify-between gap-2")
|
||||
(div
|
||||
("class" "flex items-center gap-2")
|
||||
(text "{{ components::avatar(username=profile.username, size=\"24px\") }}")
|
||||
(span
|
||||
(text "{{ profile.username }}")))
|
||||
(b
|
||||
("class" "notification chip")
|
||||
(text "{{ text \"auth:label.banned\" }}")))
|
||||
(div
|
||||
("class" "card flex flex-col gap-2")
|
||||
(span
|
||||
(text "{{ text \"auth:label.banned_message\" }}"))
|
||||
(div
|
||||
("class" "card w-full secondary flex gap-2")
|
||||
(a
|
||||
("href" "/")
|
||||
("class" "button red quaternary")
|
||||
(text "{{ icon \"x\" }}")
|
||||
(span
|
||||
(text "{{ text \"general:action.back\" }}")))))))
|
||||
|
||||
(text "{% endblock %}")
|
|
@ -1,386 +0,0 @@
|
|||
{% extends "root.html" %} {% block head %}
|
||||
<title>{{ profile.username }} - {{ config.name }}</title>
|
||||
|
||||
<meta name="og:title" content="{{ profile.username }}" />
|
||||
<meta
|
||||
name="description"
|
||||
content="View @{{ profile.username }}'s profile on {{ config.name }}!"
|
||||
/>
|
||||
<meta
|
||||
name="og:description"
|
||||
content="View @{{ profile.username }}'s profile on {{ config.name }}!"
|
||||
/>
|
||||
|
||||
<meta property="og:type" content="profile" />
|
||||
<meta property="profile:username" content="{{ profile.username }}" />
|
||||
|
||||
<meta
|
||||
name="og:image"
|
||||
content="{{ config.host|safe }}/api/v1/auth/user/{{ profile.username }}/avatar?selector_type=username"
|
||||
/>
|
||||
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content="{{ config.host|safe }}/api/v1/auth/user/{{ profile.username }}/avatar?selector_type=username"
|
||||
/>
|
||||
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:title" content="{{ profile.username }}" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="View @{{ profile.username }}'s profile on {{ config.name }}!"
|
||||
/>
|
||||
{% endblock %} {% block body %} {{ macros::nav() }}
|
||||
<article>
|
||||
<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="width: 22rem; min-width: 22rem"
|
||||
>
|
||||
<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 flex-wrap w-full">
|
||||
<span class="name shorter">{{ components::username(user=profile) }}</span>
|
||||
|
||||
{% if profile.is_verified -%}
|
||||
<span title="Verified" style="color: var(--color-primary);" class="flex items-center">
|
||||
{{ icon "badge-check" }}
|
||||
</span>
|
||||
{%- endif %}
|
||||
|
||||
{% if profile.permissions|has_supporter -%}
|
||||
<span title="Supporter" style="color: var(--color-primary);" class="flex items-center">
|
||||
{{ icon "star" }}
|
||||
</span>
|
||||
{%- endif %}
|
||||
|
||||
{% if profile.permissions|has_staff_badge -%}
|
||||
<span title="Staff" style="color: var(--color-primary);" class="flex items-center">
|
||||
{{ icon "shield-user" }}
|
||||
</span>
|
||||
{%- endif %}
|
||||
|
||||
{% if profile.permissions|has_banned -%}
|
||||
<span title="Banned" style="color: var(--color-primary);" class="flex items-center">
|
||||
{{ icon "shield-ban" }}
|
||||
</span>
|
||||
{%- endif %}
|
||||
</h3>
|
||||
|
||||
<span class="fade">{{ profile.username }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="card flex flex-col items-center gap-2"
|
||||
id="social"
|
||||
>
|
||||
{% if profile.settings.status -%}
|
||||
<p>{{ profile.settings.status }}</p>
|
||||
{%- endif %}
|
||||
|
||||
<div class="w-full flex">
|
||||
<a
|
||||
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="/@{{ 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>
|
||||
|
||||
{% if is_following_you -%}
|
||||
<b
|
||||
class="notification chip w-content flex items-center gap-2"
|
||||
>
|
||||
{{ icon "heart" }}
|
||||
<span>Follows you</span>
|
||||
</b>
|
||||
{%- endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-nest flex flex-col">
|
||||
<div id="bio" class="card small no_p_margin">
|
||||
{{ profile.settings.biography|markdown|safe }}
|
||||
</div>
|
||||
|
||||
<div class="card flex flex-col gap-2">
|
||||
<!-- prettier-ignore -->
|
||||
<div style="display: contents;">
|
||||
{% if profile.connections.Spotify and profile.connections.Spotify[0].data.name -%}
|
||||
{{ components::spotify_playing(state=profile.connections.Spotify[1]) }}
|
||||
{% elif profile.connections.LastFm and profile.connections.LastFm[0].data.name %}
|
||||
{{ components::last_fm_playing(state=profile.connections.LastFm[1]) }}
|
||||
{%- endif %}
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
||||
<div class="w-full flex justify-between items-center">
|
||||
<span class="notification chip">Posts</span>
|
||||
<span>{{ profile.post_count }}</span>
|
||||
</div>
|
||||
|
||||
{% 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>
|
||||
|
||||
<div class="flex">
|
||||
{{ components::online_indicator(user=profile) }}
|
||||
<span class="date">
|
||||
{{ profile.last_seen }}
|
||||
</span>
|
||||
</div>
|
||||
</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 -%}
|
||||
<button
|
||||
onclick="toggle_follow_user(event)"
|
||||
class="{% if is_following %} hidden{% endif %}"
|
||||
atto_tag="user.follow"
|
||||
>
|
||||
{{ icon "user-plus" }}
|
||||
<span>{{ text "auth:action.follow" }}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onclick="toggle_follow_user(event)"
|
||||
class="quaternary red{% if not is_following %} hidden{% endif %}"
|
||||
atto_tag="user.unfollow"
|
||||
>
|
||||
{{ icon "user-minus" }}
|
||||
<span>{{ text "auth:action.unfollow" }}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onclick="toggle_block_user()"
|
||||
class="quaternary red"
|
||||
>
|
||||
{{ icon "shield" }}
|
||||
<span>{{ text "auth:action.block" }}</span>
|
||||
</button>
|
||||
{% else %}
|
||||
<button
|
||||
onclick="toggle_block_user()"
|
||||
class="quaternary red"
|
||||
>
|
||||
{{ icon "shield-off" }}
|
||||
<span>{{ text "auth:action.unblock" }}</span>
|
||||
</button>
|
||||
{%- endif %} {% if not user.settings.private_chats or
|
||||
is_following_you %}
|
||||
<button
|
||||
onclick="create_group_chat()"
|
||||
class="quaternary"
|
||||
>
|
||||
{{ icon "message-circle" }}
|
||||
<span>{{ text "auth:action.message" }}</span>
|
||||
</button>
|
||||
{%- endif %} {% if is_helper -%}
|
||||
<a
|
||||
href="/mod_panel/profile/{{ profile.id }}"
|
||||
class="button quaternary"
|
||||
>
|
||||
{{ icon "shield" }}
|
||||
<span>{{ text "general:action.manage" }}</span>
|
||||
</a>
|
||||
{%- endif %}
|
||||
|
||||
<script>
|
||||
globalThis.create_group_chat = async () => {
|
||||
fetch("/api/v1/channels/group", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
title: "{{ user.username }} & {{ profile.username }}",
|
||||
members: ["{{ profile.id }}"],
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
trigger("atto::toast", [
|
||||
res.ok ? "success" : "error",
|
||||
res.message,
|
||||
]);
|
||||
|
||||
if (res.ok) {
|
||||
window.location.href = `/chats/0/${res.payload}`;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
globalThis.toggle_follow_user = async (e) => {
|
||||
await trigger("atto::debounce", [
|
||||
"users::follow",
|
||||
]);
|
||||
|
||||
fetch(
|
||||
"/api/v1/auth/user/{{ profile.id }}/follow",
|
||||
{
|
||||
method: "POST",
|
||||
},
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
trigger("atto::toast", [
|
||||
res.ok ? "success" : "error",
|
||||
res.message,
|
||||
]);
|
||||
|
||||
if (
|
||||
e.target.getAttribute(
|
||||
"atto_tag",
|
||||
) === "user.follow"
|
||||
) {
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag="user.follow"]',
|
||||
)
|
||||
.classList.add("hidden");
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag="user.unfollow"]',
|
||||
)
|
||||
.classList.remove("hidden");
|
||||
} else {
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag="user.unfollow"]',
|
||||
)
|
||||
.classList.add("hidden");
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag="user.follow"]',
|
||||
)
|
||||
.classList.remove("hidden");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
globalThis.toggle_block_user = async () => {
|
||||
if (
|
||||
!(await trigger("atto::confirm", [
|
||||
"Are you sure you would like to do this?",
|
||||
]))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(
|
||||
"/api/v1/auth/user/{{ 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 class="flex flex-col gap-2" id="connections">
|
||||
{% for key, value in profile.connections %} {% if
|
||||
value[0].data.name and value[0].show_on_profile %}
|
||||
<a
|
||||
class="card small flush flex items-center justify-between gap-2"
|
||||
href="{{ components::connection_url(key=key, value=value) }}"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
{{ components::connection_icon(key=key) }}
|
||||
<b>{{ value[0].data.name }}</b>
|
||||
</div>
|
||||
|
||||
<button class="camo small">
|
||||
{{ icon "external-link" }}
|
||||
</button>
|
||||
</a>
|
||||
{%- endif %} {% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rhs w-full flex flex-col gap-4">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% if not is_self and profile.settings.warning -%}
|
||||
<script>
|
||||
setTimeout(() => {
|
||||
// check for warning
|
||||
trigger("warnings::open", [
|
||||
"{{ profile.id }}",
|
||||
"{{ warning_hash }}",
|
||||
"?warning=true",
|
||||
]);
|
||||
}, 150);
|
||||
</script>
|
||||
{%- endif %} {% if not use_user_theme -%} {{ components::theme(user=profile,
|
||||
theme_preference=profile.settings.profile_theme) }} {%- endif %} {% endblock %}
|
371
crates/app/src/public/html/profile/base.lisp
Normal file
371
crates/app/src/public/html/profile/base.lisp
Normal file
|
@ -0,0 +1,371 @@
|
|||
(text "{% extends \"root.html\" %} {% block head %}")
|
||||
(title
|
||||
(text "{{ profile.username }} - {{ config.name }}"))
|
||||
|
||||
(meta
|
||||
("name" "og:title")
|
||||
("content" "{{ profile.username }}"))
|
||||
|
||||
(meta
|
||||
("name" "description")
|
||||
("content" "View @{{ profile.username }}'s profile on {{ config.name }}!"))
|
||||
|
||||
(meta
|
||||
("name" "og:description")
|
||||
("content" "View @{{ profile.username }}'s profile on {{ config.name }}!"))
|
||||
|
||||
(meta
|
||||
("property" "og:type")
|
||||
("content" "profile"))
|
||||
|
||||
(meta
|
||||
("property" "profile:username")
|
||||
("content" "{{ profile.username }}"))
|
||||
|
||||
(meta
|
||||
("name" "og:image")
|
||||
("content" "{{ config.host|safe }}/api/v1/auth/user/{{ profile.username }}/avatar?selector_type=username"))
|
||||
|
||||
(meta
|
||||
("name" "twitter:image")
|
||||
("content" "{{ config.host|safe }}/api/v1/auth/user/{{ profile.username }}/avatar?selector_type=username"))
|
||||
|
||||
(meta
|
||||
("name" "twitter:card")
|
||||
("content" "summary"))
|
||||
|
||||
(meta
|
||||
("name" "twitter:title")
|
||||
("content" "{{ profile.username }}"))
|
||||
|
||||
(meta
|
||||
("name" "twitter:description")
|
||||
("content" "View @{{ profile.username }}'s profile on {{ config.name }}!"))
|
||||
|
||||
(text "{% endblock %} {% block body %} {{ macros::nav() }}")
|
||||
(article
|
||||
(div
|
||||
("class" "content_container flex flex-col gap-4")
|
||||
(text "{{ 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" "width: 22rem; min-width: 22rem")
|
||||
(div
|
||||
("class" "card-nest w-full")
|
||||
(div
|
||||
("class" "card flex gap-2")
|
||||
("id" "user_avatar_and_name")
|
||||
(text "{{ components::avatar(username=profile.username,size=\"72px\") }}")
|
||||
(div
|
||||
("class" "flex flex-col")
|
||||
(h3
|
||||
("id" "username")
|
||||
("class" "username flex items-center gap-2 flex-wrap w-full")
|
||||
(span
|
||||
("class" "name shorter")
|
||||
(text "{{ components::username(user=profile) }}"))
|
||||
(text "{% if profile.is_verified -%}")
|
||||
(span
|
||||
("title" "Verified")
|
||||
("style" "color: var(--color-primary);")
|
||||
("class" "flex items-center")
|
||||
(text "{{ icon \"badge-check\" }}"))
|
||||
(text "{%- endif %} {% if profile.permissions|has_supporter -%}")
|
||||
(span
|
||||
("title" "Supporter")
|
||||
("style" "color: var(--color-primary);")
|
||||
("class" "flex items-center")
|
||||
(text "{{ icon \"star\" }}"))
|
||||
(text "{%- endif %} {% if profile.permissions|has_staff_badge -%}")
|
||||
(span
|
||||
("title" "Staff")
|
||||
("style" "color: var(--color-primary);")
|
||||
("class" "flex items-center")
|
||||
(text "{{ icon \"shield-user\" }}"))
|
||||
(text "{%- endif %} {% if profile.permissions|has_banned -%}")
|
||||
(span
|
||||
("title" "Banned")
|
||||
("style" "color: var(--color-primary);")
|
||||
("class" "flex items-center")
|
||||
(text "{{ icon \"shield-ban\" }}"))
|
||||
(text "{%- endif %}"))
|
||||
(span
|
||||
("class" "fade")
|
||||
(text "{{ profile.username }}"))))
|
||||
(div
|
||||
("class" "card flex flex-col items-center gap-2")
|
||||
("id" "social")
|
||||
(text "{% if profile.settings.status -%}")
|
||||
(p
|
||||
(text "{{ profile.settings.status }}"))
|
||||
(text "{%- endif %}")
|
||||
(div
|
||||
("class" "w-full flex")
|
||||
(a
|
||||
("href" "/@{{ profile.username }}/followers")
|
||||
("class" "w-full flex justify-center items-center gap-2")
|
||||
(h4
|
||||
(text "{{ profile.follower_count }}"))
|
||||
(span
|
||||
(text "{{ text \"auth:label.followers\" }}")))
|
||||
(a
|
||||
("href" "/@{{ profile.username }}/following")
|
||||
("class" "w-full flex justify-center items-center gap-2")
|
||||
(h4
|
||||
(text "{{ profile.following_count }}"))
|
||||
(span
|
||||
(text "{{ text \"auth:label.following\" }}"))))
|
||||
(text "{% if is_following_you -%}")
|
||||
(b
|
||||
("class" "notification chip w-content flex items-center gap-2")
|
||||
(text "{{ icon \"heart\" }}")
|
||||
(span
|
||||
(text "Follows you")))
|
||||
(text "{%- endif %}")))
|
||||
(div
|
||||
("class" "card-nest flex flex-col")
|
||||
(div
|
||||
("id" "bio")
|
||||
("class" "card small no_p_margin")
|
||||
(text "{{ profile.settings.biography|markdown|safe }}"))
|
||||
(div
|
||||
("class" "card flex flex-col gap-2")
|
||||
(div
|
||||
("style" "display: contents;")
|
||||
(text "{% if profile.connections.Spotify and profile.connections.Spotify[0].data.name -%} {{ components::spotify_playing(state=profile.connections.Spotify[1]) }} {% elif profile.connections.LastFm and profile.connections.LastFm[0].data.name %} {{ components::last_fm_playing(state=profile.connections.LastFm[1]) }} {%- endif %}"))
|
||||
(div
|
||||
("class" "w-full flex justify-between items-center")
|
||||
(span
|
||||
("class" "notification chip")
|
||||
(text "ID"))
|
||||
(button
|
||||
("title" "Copy")
|
||||
("onclick" "trigger('atto::copy_text', ['{{ profile.id }}'])")
|
||||
("class" "camo small")
|
||||
(text "{{ icon \"copy\" }}")))
|
||||
(div
|
||||
("class" "w-full flex justify-between items-center")
|
||||
(span
|
||||
("class" "notification chip")
|
||||
(text "Joined"))
|
||||
(span
|
||||
("class" "date")
|
||||
(text "{{ profile.created }}")))
|
||||
(div
|
||||
("class" "w-full flex justify-between items-center")
|
||||
(span
|
||||
("class" "notification chip")
|
||||
(text "Posts"))
|
||||
(span
|
||||
(text "{{ profile.post_count }}")))
|
||||
(text "{% 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")
|
||||
(text "Last seen"))
|
||||
(div
|
||||
("class" "flex")
|
||||
(text "{{ components::online_indicator(user=profile) }}")
|
||||
(span
|
||||
("class" "date")
|
||||
(text "{{ profile.last_seen }}"))))
|
||||
(text "{%- endif %}")))
|
||||
(text "{% if not is_self and user -%}")
|
||||
(div
|
||||
("class" "card-nest")
|
||||
(div
|
||||
("class" "card small")
|
||||
(b
|
||||
(text "{{ text \"auth:label.relationship\" }}")))
|
||||
(div
|
||||
("class" "card flex gap-2 flex-wrap")
|
||||
(text "{% if not is_blocking -%}")
|
||||
(button
|
||||
("onclick" "toggle_follow_user(event)")
|
||||
("class" "{% if is_following %} hidden{% endif %}")
|
||||
("atto_tag" "user.follow")
|
||||
(text "{{ icon \"user-plus\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:action.follow\" }}")))
|
||||
(button
|
||||
("onclick" "toggle_follow_user(event)")
|
||||
("class" "quaternary red{% if not is_following %} hidden{% endif %}")
|
||||
("atto_tag" "user.unfollow")
|
||||
(text "{{ icon \"user-minus\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:action.unfollow\" }}")))
|
||||
(button
|
||||
("onclick" "toggle_block_user()")
|
||||
("class" "quaternary red")
|
||||
(text "{{ icon \"shield\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:action.block\" }}")))
|
||||
(text "{% else %}")
|
||||
(button
|
||||
("onclick" "toggle_block_user()")
|
||||
("class" "quaternary red")
|
||||
(text "{{ icon \"shield-off\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:action.unblock\" }}")))
|
||||
(text "{%- endif %} {% if not user.settings.private_chats or is_following_you %}")
|
||||
(button
|
||||
("onclick" "create_group_chat()")
|
||||
("class" "quaternary")
|
||||
(text "{{ icon \"message-circle\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:action.message\" }}")))
|
||||
(text "{%- endif %} {% if is_helper -%}")
|
||||
(a
|
||||
("href" "/mod_panel/profile/{{ profile.id }}")
|
||||
("class" "button quaternary")
|
||||
(text "{{ icon \"shield\" }}")
|
||||
(span
|
||||
(text "{{ text \"general:action.manage\" }}")))
|
||||
(text "{%- endif %}")
|
||||
(script
|
||||
(text "globalThis.create_group_chat = async () => {
|
||||
fetch(\"/api/v1/channels/group\", {
|
||||
method: \"POST\",
|
||||
headers: {
|
||||
\"Content-Type\": \"application/json\",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
title: \"{{ user.username }} & {{ profile.username }}\",
|
||||
members: [\"{{ profile.id }}\"],
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
trigger(\"atto::toast\", [
|
||||
res.ok ? \"success\" : \"error\",
|
||||
res.message,
|
||||
]);
|
||||
|
||||
if (res.ok) {
|
||||
window.location.href = `/chats/0/${res.payload}`;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
globalThis.toggle_follow_user = async (e) => {
|
||||
await trigger(\"atto::debounce\", [
|
||||
\"users::follow\",
|
||||
]);
|
||||
|
||||
fetch(
|
||||
\"/api/v1/auth/user/{{ profile.id }}/follow\",
|
||||
{
|
||||
method: \"POST\",
|
||||
},
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
trigger(\"atto::toast\", [
|
||||
res.ok ? \"success\" : \"error\",
|
||||
res.message,
|
||||
]);
|
||||
|
||||
if (
|
||||
e.target.getAttribute(
|
||||
\"atto_tag\",
|
||||
) === \"user.follow\"
|
||||
) {
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag=\"user.follow\"]',
|
||||
)
|
||||
.classList.add(\"hidden\");
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag=\"user.unfollow\"]',
|
||||
)
|
||||
.classList.remove(\"hidden\");
|
||||
} else {
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag=\"user.unfollow\"]',
|
||||
)
|
||||
.classList.add(\"hidden\");
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag=\"user.follow\"]',
|
||||
)
|
||||
.classList.remove(\"hidden\");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
globalThis.toggle_block_user = async () => {
|
||||
if (
|
||||
!(await trigger(\"atto::confirm\", [
|
||||
\"Are you sure you would like to do this?\",
|
||||
]))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(
|
||||
\"/api/v1/auth/user/{{ profile.id }}/block\",
|
||||
{
|
||||
method: \"POST\",
|
||||
},
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
trigger(\"atto::toast\", [
|
||||
res.ok ? \"success\" : \"error\",
|
||||
res.message,
|
||||
]);
|
||||
});
|
||||
};"))))
|
||||
(text "{%- 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")
|
||||
(text "{{ icon \"users-round\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:label.joined_communities\" }}")))
|
||||
(div
|
||||
("class" "card flex flex-wrap gap-2")
|
||||
(text "{% for community in communities %}")
|
||||
(a
|
||||
("href" "/community/{{ community.title }}")
|
||||
(text "{{ components::community_avatar(id=community.id, community=community, size=\"48px\") }}"))
|
||||
(text "{% endfor %}")))
|
||||
(text "{%- endif %}")
|
||||
(div
|
||||
("class" "flex flex-col gap-2")
|
||||
("id" "connections")
|
||||
(text "{% for key, value in profile.connections %} {% if value[0].data.name and value[0].show_on_profile %}")
|
||||
(a
|
||||
("class" "card small flush flex items-center justify-between gap-2")
|
||||
("href" "{{ components::connection_url(key=key, value=value) }}")
|
||||
(div
|
||||
("class" "flex items-center gap-2")
|
||||
(text "{{ components::connection_icon(key=key) }}")
|
||||
(b
|
||||
(text "{{ value[0].data.name }}")))
|
||||
(button
|
||||
("class" "camo small")
|
||||
(text "{{ icon \"external-link\" }}")))
|
||||
(text "{%- endif %} {% endfor %}")))
|
||||
(div
|
||||
("class" "rhs w-full flex flex-col gap-4")
|
||||
(text "{% block content %}{% endblock %}")))))
|
||||
|
||||
(text "{% if not is_self and profile.settings.warning -%}")
|
||||
(script
|
||||
(text "setTimeout(() => {
|
||||
// check for warning
|
||||
trigger(\"warnings::open\", [
|
||||
\"{{ profile.id }}\",
|
||||
\"{{ warning_hash }}\",
|
||||
\"?warning=true\",
|
||||
]);
|
||||
}, 150);"))
|
||||
|
||||
(text "{%- endif %} {% if not use_user_theme -%} {{ components::theme(user=profile, theme_preference=profile.settings.profile_theme) }} {%- endif %} {% endblock %}")
|
|
@ -1,65 +0,0 @@
|
|||
{% extends "root.html" %} {% block head %}
|
||||
<title>{{ profile.username }} (blocked) - {{ config.name }}</title>
|
||||
{% endblock %} {% block body %} {{ macros::nav() }}
|
||||
<main class="flex flex-col gap-2">
|
||||
<div class="card-nest">
|
||||
<div class="card small flex items-center justify-between gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
{{ components::avatar(username=profile.username, size="24px") }}
|
||||
<span>{{ profile.username }}</span>
|
||||
</div>
|
||||
|
||||
<b class="notification chip"
|
||||
>{{ text "auth:label.blocked_profile" }}</b
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="card flex flex-col gap-2">
|
||||
<span>{{ text "auth:label.blocked_profile_message" }}</span>
|
||||
|
||||
<div class="card w-full secondary flex gap-2">
|
||||
{% if user -%} {% if not is_blocking -%}
|
||||
<button onclick="toggle_block_user()" class="quaternary red">
|
||||
{{ icon "shield" }}
|
||||
<span>{{ text "auth:action.block" }}</span>
|
||||
</button>
|
||||
{% else %}
|
||||
<button onclick="toggle_block_user()" class="quaternary red">
|
||||
{{ icon "shield-off" }}
|
||||
<span>{{ text "auth:action.unblock" }}</span>
|
||||
</button>
|
||||
{%- endif %}
|
||||
|
||||
<script>
|
||||
globalThis.toggle_block_user = async () => {
|
||||
if (
|
||||
!(await trigger("atto::confirm", [
|
||||
"Are you sure you would like to do this?",
|
||||
]))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch("/api/v1/auth/user/{{ profile.id }}/block", {
|
||||
method: "POST",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
trigger("atto::toast", [
|
||||
res.ok ? "success" : "error",
|
||||
res.message,
|
||||
]);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
{%- endif %}
|
||||
|
||||
<a href="/" class="button red quaternary">
|
||||
{{ icon "x" }}
|
||||
<span>{{ text "general:action.back" }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
70
crates/app/src/public/html/profile/blocked.lisp
Normal file
70
crates/app/src/public/html/profile/blocked.lisp
Normal file
|
@ -0,0 +1,70 @@
|
|||
(text "{% extends \"root.html\" %} {% block head %}")
|
||||
(title
|
||||
(text "{{ profile.username }} (blocked) - {{ config.name }}"))
|
||||
|
||||
(text "{% endblock %} {% block body %} {{ macros::nav() }}")
|
||||
(main
|
||||
("class" "flex flex-col gap-2")
|
||||
(div
|
||||
("class" "card-nest")
|
||||
(div
|
||||
("class" "card small flex items-center justify-between gap-2")
|
||||
(div
|
||||
("class" "flex items-center gap-2")
|
||||
(text "{{ components::avatar(username=profile.username, size=\"24px\") }}")
|
||||
(span
|
||||
(text "{{ profile.username }}")))
|
||||
(b
|
||||
("class" "notification chip")
|
||||
(text "{{ text \"auth:label.blocked_profile\" }}")))
|
||||
(div
|
||||
("class" "card flex flex-col gap-2")
|
||||
(span
|
||||
(text "{{ text \"auth:label.blocked_profile_message\" }}"))
|
||||
(div
|
||||
("class" "card w-full secondary flex gap-2")
|
||||
(text "{% if user -%} {% if not is_blocking -%}")
|
||||
(button
|
||||
("onclick" "toggle_block_user()")
|
||||
("class" "quaternary red")
|
||||
(text "{{ icon \"shield\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:action.block\" }}")))
|
||||
(text "{% else %}")
|
||||
(button
|
||||
("onclick" "toggle_block_user()")
|
||||
("class" "quaternary red")
|
||||
(text "{{ icon \"shield-off\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:action.unblock\" }}")))
|
||||
(text "{%- endif %}")
|
||||
(script
|
||||
(text "globalThis.toggle_block_user = async () => {
|
||||
if (
|
||||
!(await trigger(\"atto::confirm\", [
|
||||
\"Are you sure you would like to do this?\",
|
||||
]))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(\"/api/v1/auth/user/{{ profile.id }}/block\", {
|
||||
method: \"POST\",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
trigger(\"atto::toast\", [
|
||||
res.ok ? \"success\" : \"error\",
|
||||
res.message,
|
||||
]);
|
||||
});
|
||||
};"))
|
||||
(text "{%- endif %}")
|
||||
(a
|
||||
("href" "/")
|
||||
("class" "button red quaternary")
|
||||
(text "{{ icon \"x\" }}")
|
||||
(span
|
||||
(text "{{ text \"general:action.back\" }}")))))))
|
||||
|
||||
(text "{% endblock %}")
|
|
@ -1,27 +0,0 @@
|
|||
{% 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-wrap gap-4 flex-collapse">
|
||||
<!-- prettier-ignore -->
|
||||
{% for item in list %}
|
||||
{{ components::user_plate(user=item[1], secondary=true) }}
|
||||
{% endfor %} {{ components::pagination(page=page, items=list|length) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.user_plate {
|
||||
width: calc(50% - 0.5rem);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.user_plate {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
24
crates/app/src/public/html/profile/followers.lisp
Normal file
24
crates/app/src/public/html/profile/followers.lisp
Normal file
|
@ -0,0 +1,24 @@
|
|||
(text "{% extends \"profile/base.html\" %} {% block content %}")
|
||||
(div
|
||||
("class" "card-nest")
|
||||
(div
|
||||
("class" "card small flex gap-2 items-center")
|
||||
(text "{{ icon \"users-round\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:label.followers\" }}")))
|
||||
(div
|
||||
("class" "card flex flex-wrap gap-4 flex-collapse")
|
||||
(text "{% for item in list %} {{ components::user_plate(user=item[1], secondary=true) }} {% endfor %} {{ components::pagination(page=page, items=list|length) }}")))
|
||||
|
||||
(style
|
||||
(text ".user_plate {
|
||||
width: calc(50% - 0.5rem);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.user_plate {
|
||||
width: 100%;
|
||||
}
|
||||
}"))
|
||||
|
||||
(text "{% endblock %}")
|
|
@ -1,27 +0,0 @@
|
|||
{% 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-wrap gap-4 flex-collapse">
|
||||
<!-- prettier-ignore -->
|
||||
{% for item in list %}
|
||||
{{ components::user_plate(user=item[1], secondary=true) }}
|
||||
{% endfor %} {{ components::pagination(page=page, items=list|length) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.user_plate {
|
||||
width: calc(50% - 0.5rem);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.user_plate {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
24
crates/app/src/public/html/profile/following.lisp
Normal file
24
crates/app/src/public/html/profile/following.lisp
Normal file
|
@ -0,0 +1,24 @@
|
|||
(text "{% extends \"profile/base.html\" %} {% block content %}")
|
||||
(div
|
||||
("class" "card-nest")
|
||||
(div
|
||||
("class" "card small flex gap-2 items-center")
|
||||
(text "{{ icon \"users-round\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:label.following\" }}")))
|
||||
(div
|
||||
("class" "card flex flex-wrap gap-4 flex-collapse")
|
||||
(text "{% for item in list %} {{ components::user_plate(user=item[1], secondary=true) }} {% endfor %} {{ components::pagination(page=page, items=list|length) }}")))
|
||||
|
||||
(style
|
||||
(text ".user_plate {
|
||||
width: calc(50% - 0.5rem);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.user_plate {
|
||||
width: 100%;
|
||||
}
|
||||
}"))
|
||||
|
||||
(text "{% endblock %}")
|
|
@ -1,42 +0,0 @@
|
|||
{% extends "profile/base.html" %} {% block content %} {% if
|
||||
profile.settings.enable_questions and (user or
|
||||
profile.settings.allow_anonymous_questions) %}
|
||||
<div style="display: contents">
|
||||
{{ components::create_question_form(receiver=profile.id,
|
||||
header=profile.settings.motivational_header) }}
|
||||
</div>
|
||||
{%- endif %} {{ macros::profile_nav(selected="media") }}
|
||||
<div class="card-nest">
|
||||
<div class="card small flex gap-2 justify-between items-center">
|
||||
<div class="flex gap-2 items-center">
|
||||
{{ icon "clock" }}
|
||||
<span>{{ text "auth:label.recent_posts_with_media" }}</span>
|
||||
</div>
|
||||
|
||||
{% if user -%}
|
||||
<a
|
||||
href="/search?profile={{ profile.id }}"
|
||||
class="button quaternary small"
|
||||
>
|
||||
{{ icon "search" }}
|
||||
<span>{{ text "general:link.search" }}</span>
|
||||
</a>
|
||||
{%- endif %}
|
||||
</div>
|
||||
|
||||
<div class="card flex flex-col gap-4">
|
||||
<!-- prettier-ignore -->
|
||||
{% for post in posts %}
|
||||
{% if post[2].read_access == "Everybody" -%}
|
||||
{% if post[0].context.repost and post[0].context.repost.reposting -%}
|
||||
{{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }}
|
||||
{% else %}
|
||||
{{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self) }}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
|
||||
{{ components::pagination(page=page, items=posts|length) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
28
crates/app/src/public/html/profile/media.lisp
Normal file
28
crates/app/src/public/html/profile/media.lisp
Normal file
|
@ -0,0 +1,28 @@
|
|||
(text "{% extends \"profile/base.html\" %} {% block content %} {% if profile.settings.enable_questions and (user or profile.settings.allow_anonymous_questions) %}")
|
||||
(div
|
||||
("style" "display: contents")
|
||||
(text "{{ components::create_question_form(receiver=profile.id, header=profile.settings.motivational_header) }}"))
|
||||
|
||||
(text "{%- endif %} {{ macros::profile_nav(selected=\"media\") }}")
|
||||
(div
|
||||
("class" "card-nest")
|
||||
(div
|
||||
("class" "card small flex gap-2 justify-between items-center")
|
||||
(div
|
||||
("class" "flex gap-2 items-center")
|
||||
(text "{{ icon \"clock\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:label.recent_posts_with_media\" }}")))
|
||||
(text "{% if user -%}")
|
||||
(a
|
||||
("href" "/search?profile={{ profile.id }}")
|
||||
("class" "button quaternary small")
|
||||
(text "{{ icon \"search\" }}")
|
||||
(span
|
||||
(text "{{ text \"general:link.search\" }}")))
|
||||
(text "{%- endif %}"))
|
||||
(div
|
||||
("class" "card flex flex-col gap-4")
|
||||
(text "{% for post in posts %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=posts|length) }}")))
|
||||
|
||||
(text "{% endblock %}")
|
|
@ -1,67 +0,0 @@
|
|||
{% extends "profile/base.html" %} {% block content %} {% if
|
||||
profile.settings.enable_questions and (user or
|
||||
profile.settings.allow_anonymous_questions) %}
|
||||
<div style="display: contents">
|
||||
{{ components::create_question_form(receiver=profile.id,
|
||||
header=profile.settings.motivational_header) }}
|
||||
</div>
|
||||
{%- endif %} {% if not tag and pinned|length != 0 -%}
|
||||
<div class="card-nest">
|
||||
<div class="card small flex gap-2 items-center">
|
||||
{{ icon "pin" }}
|
||||
<span>{{ text "communities:label.pinned" }}</span>
|
||||
</div>
|
||||
|
||||
<div class="card flex flex-col gap-4">
|
||||
<!-- prettier-ignore -->
|
||||
{% for post in pinned %}
|
||||
{% if post[2].read_access == "Everybody" -%}
|
||||
{% if post[0].context.repost and post[0].context.repost.reposting -%}
|
||||
{{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }}
|
||||
{% else %}
|
||||
{{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self) }}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{%- endif %} {{ macros::profile_nav(selected="posts") }}
|
||||
<div class="card-nest">
|
||||
<div class="card small flex gap-2 justify-between items-center">
|
||||
<div class="flex gap-2 items-center">
|
||||
{% if not tag -%} {{ icon "clock" }}
|
||||
<span>{{ text "auth:label.recent_posts" }}</span>
|
||||
{% else %} {{ icon "tag" }}
|
||||
<span
|
||||
>{{ text "auth:label.recent_with_tag" }}: <b>{{ tag }}</b></span
|
||||
>
|
||||
{%- endif %}
|
||||
</div>
|
||||
|
||||
{% if user -%}
|
||||
<a
|
||||
href="/search?profile={{ profile.id }}"
|
||||
class="button quaternary small"
|
||||
>
|
||||
{{ icon "search" }}
|
||||
<span>{{ text "general:link.search" }}</span>
|
||||
</a>
|
||||
{%- endif %}
|
||||
</div>
|
||||
|
||||
<div class="card flex flex-col gap-4">
|
||||
<!-- prettier-ignore -->
|
||||
{% for post in posts %}
|
||||
{% if post[2].read_access == "Everybody" -%}
|
||||
{% if post[0].context.repost and post[0].context.repost.reposting -%}
|
||||
{{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }}
|
||||
{% else %}
|
||||
{{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self) }}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
|
||||
{{ components::pagination(page=page, items=posts|length, key="&tag=", value=tag) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
46
crates/app/src/public/html/profile/posts.lisp
Normal file
46
crates/app/src/public/html/profile/posts.lisp
Normal file
|
@ -0,0 +1,46 @@
|
|||
(text "{% extends \"profile/base.html\" %} {% block content %} {% if profile.settings.enable_questions and (user or profile.settings.allow_anonymous_questions) %}")
|
||||
(div
|
||||
("style" "display: contents")
|
||||
(text "{{ components::create_question_form(receiver=profile.id, header=profile.settings.motivational_header) }}"))
|
||||
|
||||
(text "{%- endif %} {% if not tag and pinned|length != 0 -%}")
|
||||
(div
|
||||
("class" "card-nest")
|
||||
(div
|
||||
("class" "card small flex gap-2 items-center")
|
||||
(text "{{ icon \"pin\" }}")
|
||||
(span
|
||||
(text "{{ text \"communities:label.pinned\" }}")))
|
||||
(div
|
||||
("class" "card flex flex-col gap-4")
|
||||
(text "{% for post in pinned %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self) }} {%- endif %} {%- endif %} {% endfor %}")))
|
||||
|
||||
(text "{%- endif %} {{ macros::profile_nav(selected=\"posts\") }}")
|
||||
(div
|
||||
("class" "card-nest")
|
||||
(div
|
||||
("class" "card small flex gap-2 justify-between items-center")
|
||||
(div
|
||||
("class" "flex gap-2 items-center")
|
||||
(text "{% if not tag -%} {{ icon \"clock\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:label.recent_posts\" }}"))
|
||||
(text "{% else %} {{ icon \"tag\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:label.recent_with_tag\" }}:")
|
||||
(b
|
||||
(text "{{ tag }}")))
|
||||
(text "{%- endif %}"))
|
||||
(text "{% if user -%}")
|
||||
(a
|
||||
("href" "/search?profile={{ profile.id }}")
|
||||
("class" "button quaternary small")
|
||||
(text "{{ icon \"search\" }}")
|
||||
(span
|
||||
(text "{{ text \"general:link.search\" }}")))
|
||||
(text "{%- endif %}"))
|
||||
(div
|
||||
("class" "card flex flex-col gap-4")
|
||||
(text "{% for post in posts %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=posts|length, key=\"&tag=\", value=tag) }}")))
|
||||
|
||||
(text "{% endblock %}")
|
|
@ -1,162 +0,0 @@
|
|||
{% extends "root.html" %} {% block head %}
|
||||
<title>{{ profile.username }} (private profile) - {{ config.name }}</title>
|
||||
{% endblock %} {% block body %} {{ macros::nav() }}
|
||||
<main class="flex flex-col gap-2">
|
||||
<div class="card-nest">
|
||||
<div class="card small flex items-center justify-between gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
{{ components::avatar(username=profile.username, size="24px") }}
|
||||
<span>{{ profile.username }}</span>
|
||||
</div>
|
||||
|
||||
<b class="notification chip"
|
||||
>{{ text "auth:label.private_profile" }}</b
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="card flex flex-col gap-2">
|
||||
<span>{{ text "auth:label.private_profile_message" }}</span>
|
||||
|
||||
<div class="card w-full secondary flex gap-2">
|
||||
{% if user -%} {% if not is_following -%}
|
||||
<button
|
||||
onclick="toggle_follow_user(event)"
|
||||
class="{% if follow_requested -%} hidden{%- endif %}"
|
||||
atto_tag="user.follow_request"
|
||||
>
|
||||
{{ icon "user-plus" }}
|
||||
<span>{{ text "auth:action.request_to_follow" }}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onclick="cancel_follow_user(event)"
|
||||
class="quaternary red{% if not follow_requested -%} hidden{%- endif %}"
|
||||
atto_tag="user.cancel_request"
|
||||
>
|
||||
{{ icon "user-minus" }}
|
||||
<span>{{ text "auth:action.cancel_follow_request" }}</span>
|
||||
</button>
|
||||
{% else %}
|
||||
<button
|
||||
onclick="toggle_follow_user(event)"
|
||||
class="quaternary red"
|
||||
atto_tag="user.unfollow"
|
||||
>
|
||||
{{ icon "user-minus" }}
|
||||
<span>{{ text "auth:action.unfollow" }}</span>
|
||||
</button>
|
||||
{%- endif %} {% if not is_blocking -%}
|
||||
<button onclick="toggle_block_user()" class="quaternary red">
|
||||
{{ icon "shield" }}
|
||||
<span>{{ text "auth:action.block" }}</span>
|
||||
</button>
|
||||
{% else %}
|
||||
<button onclick="toggle_block_user()" class="quaternary red">
|
||||
{{ icon "shield-off" }}
|
||||
<span>{{ text "auth:action.unblock" }}</span>
|
||||
</button>
|
||||
{%- endif %}
|
||||
|
||||
<script>
|
||||
globalThis.toggle_follow_user = async (e) => {
|
||||
await trigger("atto::debounce", ["users::follow"]);
|
||||
fetch("/api/v1/auth/user/{{ profile.id }}/follow", {
|
||||
method: "POST",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
trigger("atto::toast", [
|
||||
res.ok ? "success" : "error",
|
||||
res.message,
|
||||
]);
|
||||
|
||||
if (
|
||||
e.target.getAttribute("atto_tag") ===
|
||||
"user.follow_request"
|
||||
) {
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag="user.follow_request"]',
|
||||
)
|
||||
.classList.add("hidden");
|
||||
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag="user.cancel_request"]',
|
||||
)
|
||||
.classList.remove("hidden");
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
globalThis.cancel_follow_user = async (e) => {
|
||||
await trigger("atto::debounce", ["users::follow"]);
|
||||
|
||||
if (
|
||||
!(await trigger("atto::confirm", [
|
||||
"Are you sure you would like to do this?",
|
||||
]))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(
|
||||
"/api/v1/auth/user/{{ profile.id }}/follow/cancel",
|
||||
{
|
||||
method: "POST",
|
||||
},
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
trigger("atto::toast", [
|
||||
res.ok ? "success" : "error",
|
||||
res.message,
|
||||
]);
|
||||
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag="user.cancel_request"]',
|
||||
)
|
||||
.classList.add("hidden");
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag="user.follow_request"]',
|
||||
)
|
||||
.classList.remove("hidden");
|
||||
});
|
||||
};
|
||||
|
||||
globalThis.toggle_block_user = async () => {
|
||||
if (
|
||||
!(await trigger("atto::confirm", [
|
||||
"Are you sure you would like to do this?",
|
||||
]))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch("/api/v1/auth/user/{{ profile.id }}/block", {
|
||||
method: "POST",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
trigger("atto::toast", [
|
||||
res.ok ? "success" : "error",
|
||||
res.message,
|
||||
]);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
{%- endif %}
|
||||
|
||||
<a href="/" class="button red quaternary">
|
||||
{{ icon "x" }}
|
||||
<span>{{ text "general:action.back" }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
163
crates/app/src/public/html/profile/private.lisp
Normal file
163
crates/app/src/public/html/profile/private.lisp
Normal file
|
@ -0,0 +1,163 @@
|
|||
(text "{% extends \"root.html\" %} {% block head %}")
|
||||
(title
|
||||
(text "{{ profile.username }} (private profile) - {{ config.name }}"))
|
||||
|
||||
(text "{% endblock %} {% block body %} {{ macros::nav() }}")
|
||||
(main
|
||||
("class" "flex flex-col gap-2")
|
||||
(div
|
||||
("class" "card-nest")
|
||||
(div
|
||||
("class" "card small flex items-center justify-between gap-2")
|
||||
(div
|
||||
("class" "flex items-center gap-2")
|
||||
(text "{{ components::avatar(username=profile.username, size=\"24px\") }}")
|
||||
(span
|
||||
(text "{{ profile.username }}")))
|
||||
(b
|
||||
("class" "notification chip")
|
||||
(text "{{ text \"auth:label.private_profile\" }}")))
|
||||
(div
|
||||
("class" "card flex flex-col gap-2")
|
||||
(span
|
||||
(text "{{ text \"auth:label.private_profile_message\" }}"))
|
||||
(div
|
||||
("class" "card w-full secondary flex gap-2")
|
||||
(text "{% if user -%} {% if not is_following -%}")
|
||||
(button
|
||||
("onclick" "toggle_follow_user(event)")
|
||||
("class" "{% if follow_requested -%} hidden{%- endif %}")
|
||||
("atto_tag" "user.follow_request")
|
||||
(text "{{ icon \"user-plus\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:action.request_to_follow\" }}")))
|
||||
(button
|
||||
("onclick" "cancel_follow_user(event)")
|
||||
("class" "quaternary red{% if not follow_requested -%} hidden{%- endif %}")
|
||||
("atto_tag" "user.cancel_request")
|
||||
(text "{{ icon \"user-minus\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:action.cancel_follow_request\" }}")))
|
||||
(text "{% else %}")
|
||||
(button
|
||||
("onclick" "toggle_follow_user(event)")
|
||||
("class" "quaternary red")
|
||||
("atto_tag" "user.unfollow")
|
||||
(text "{{ icon \"user-minus\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:action.unfollow\" }}")))
|
||||
(text "{%- endif %} {% if not is_blocking -%}")
|
||||
(button
|
||||
("onclick" "toggle_block_user()")
|
||||
("class" "quaternary red")
|
||||
(text "{{ icon \"shield\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:action.block\" }}")))
|
||||
(text "{% else %}")
|
||||
(button
|
||||
("onclick" "toggle_block_user()")
|
||||
("class" "quaternary red")
|
||||
(text "{{ icon \"shield-off\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:action.unblock\" }}")))
|
||||
(text "{%- endif %}")
|
||||
(script
|
||||
(text "globalThis.toggle_follow_user = async (e) => {
|
||||
await trigger(\"atto::debounce\", [\"users::follow\"]);
|
||||
fetch(\"/api/v1/auth/user/{{ profile.id }}/follow\", {
|
||||
method: \"POST\",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
trigger(\"atto::toast\", [
|
||||
res.ok ? \"success\" : \"error\",
|
||||
res.message,
|
||||
]);
|
||||
|
||||
if (
|
||||
e.target.getAttribute(\"atto_tag\") ===
|
||||
\"user.follow_request\"
|
||||
) {
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag=\"user.follow_request\"]',
|
||||
)
|
||||
.classList.add(\"hidden\");
|
||||
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag=\"user.cancel_request\"]',
|
||||
)
|
||||
.classList.remove(\"hidden\");
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
globalThis.cancel_follow_user = async (e) => {
|
||||
await trigger(\"atto::debounce\", [\"users::follow\"]);
|
||||
|
||||
if (
|
||||
!(await trigger(\"atto::confirm\", [
|
||||
\"Are you sure you would like to do this?\",
|
||||
]))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(
|
||||
\"/api/v1/auth/user/{{ profile.id }}/follow/cancel\",
|
||||
{
|
||||
method: \"POST\",
|
||||
},
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
trigger(\"atto::toast\", [
|
||||
res.ok ? \"success\" : \"error\",
|
||||
res.message,
|
||||
]);
|
||||
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag=\"user.cancel_request\"]',
|
||||
)
|
||||
.classList.add(\"hidden\");
|
||||
document
|
||||
.querySelector(
|
||||
'[atto_tag=\"user.follow_request\"]',
|
||||
)
|
||||
.classList.remove(\"hidden\");
|
||||
});
|
||||
};
|
||||
|
||||
globalThis.toggle_block_user = async () => {
|
||||
if (
|
||||
!(await trigger(\"atto::confirm\", [
|
||||
\"Are you sure you would like to do this?\",
|
||||
]))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(\"/api/v1/auth/user/{{ profile.id }}/block\", {
|
||||
method: \"POST\",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
trigger(\"atto::toast\", [
|
||||
res.ok ? \"success\" : \"error\",
|
||||
res.message,
|
||||
]);
|
||||
});
|
||||
};"))
|
||||
(text "{%- endif %}")
|
||||
(a
|
||||
("href" "/")
|
||||
("class" "button red quaternary")
|
||||
(text "{{ icon \"x\" }}")
|
||||
(span
|
||||
(text "{{ text \"general:action.back\" }}")))))))
|
||||
|
||||
(text "{% endblock %}")
|
|
@ -1,42 +0,0 @@
|
|||
{% extends "profile/base.html" %} {% block content %} {% if
|
||||
profile.settings.enable_questions and (user or
|
||||
profile.settings.allow_anonymous_questions) %}
|
||||
<div style="display: contents">
|
||||
{{ components::create_question_form(receiver=profile.id,
|
||||
header=profile.settings.motivational_header) }}
|
||||
</div>
|
||||
{%- endif %} {{ macros::profile_nav(selected="replies") }}
|
||||
<div class="card-nest">
|
||||
<div class="card small flex gap-2 justify-between items-center">
|
||||
<div class="flex gap-2 items-center">
|
||||
{{ icon "clock" }}
|
||||
<span>{{ text "auth:label.recent_replies" }}</span>
|
||||
</div>
|
||||
|
||||
{% if user -%}
|
||||
<a
|
||||
href="/search?profile={{ profile.id }}"
|
||||
class="button quaternary small"
|
||||
>
|
||||
{{ icon "search" }}
|
||||
<span>{{ text "general:link.search" }}</span>
|
||||
</a>
|
||||
{%- endif %}
|
||||
</div>
|
||||
|
||||
<div class="card flex flex-col gap-4">
|
||||
<!-- prettier-ignore -->
|
||||
{% for post in posts %}
|
||||
{% if post[2].read_access == "Everybody" -%}
|
||||
{% if post[0].context.repost and post[0].context.repost.reposting -%}
|
||||
{{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }}
|
||||
{% else %}
|
||||
{{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self) }}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
|
||||
{{ components::pagination(page=page, items=posts|length) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
28
crates/app/src/public/html/profile/replies.lisp
Normal file
28
crates/app/src/public/html/profile/replies.lisp
Normal file
|
@ -0,0 +1,28 @@
|
|||
(text "{% extends \"profile/base.html\" %} {% block content %} {% if profile.settings.enable_questions and (user or profile.settings.allow_anonymous_questions) %}")
|
||||
(div
|
||||
("style" "display: contents")
|
||||
(text "{{ components::create_question_form(receiver=profile.id, header=profile.settings.motivational_header) }}"))
|
||||
|
||||
(text "{%- endif %} {{ macros::profile_nav(selected=\"replies\") }}")
|
||||
(div
|
||||
("class" "card-nest")
|
||||
(div
|
||||
("class" "card small flex gap-2 justify-between items-center")
|
||||
(div
|
||||
("class" "flex gap-2 items-center")
|
||||
(text "{{ icon \"clock\" }}")
|
||||
(span
|
||||
(text "{{ text \"auth:label.recent_replies\" }}")))
|
||||
(text "{% if user -%}")
|
||||
(a
|
||||
("href" "/search?profile={{ profile.id }}")
|
||||
("class" "button quaternary small")
|
||||
(text "{{ icon \"search\" }}")
|
||||
(span
|
||||
(text "{{ text \"general:link.search\" }}")))
|
||||
(text "{%- endif %}"))
|
||||
(div
|
||||
("class" "card flex flex-col gap-4")
|
||||
(text "{% for post in posts %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=posts|length) }}")))
|
||||
|
||||
(text "{% endblock %}")
|
File diff suppressed because it is too large
Load diff
1544
crates/app/src/public/html/profile/settings.lisp
Normal file
1544
crates/app/src/public/html/profile/settings.lisp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,37 +0,0 @@
|
|||
{% extends "root.html" %} {% block head %}
|
||||
<title>{{ profile.username }} (warning) - {{ config.name }}</title>
|
||||
{% endblock %} {% block body %} {{ macros::nav() }}
|
||||
<main class="flex flex-col gap-2">
|
||||
<div class="card-nest">
|
||||
<div class="card small flex items-center justify-between gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
{{ components::avatar(username=profile.username, size="24px") }}
|
||||
<span>{{ profile.username }}</span>
|
||||
</div>
|
||||
|
||||
<b class="notification chip"
|
||||
>{{ text "auth:label.before_you_view" }}</b
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="card flex flex-col gap-2">
|
||||
<span class="no_p_margin"
|
||||
>{{ profile.settings.warning|markdown|safe }}</span
|
||||
>
|
||||
<div class="card w-full secondary flex gap-2">
|
||||
<button
|
||||
onclick="trigger('warnings::accept', ['{{ profile.id }}', '{{ warning_hash }}'])"
|
||||
>
|
||||
{{ icon "check" }}
|
||||
<span>{{ text "dialog:action.continue" }}</span>
|
||||
</button>
|
||||
|
||||
<a href="/" class="button red quaternary">
|
||||
{{ icon "x" }}
|
||||
<span>{{ text "general:action.back" }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
39
crates/app/src/public/html/profile/warning.lisp
Normal file
39
crates/app/src/public/html/profile/warning.lisp
Normal file
|
@ -0,0 +1,39 @@
|
|||
(text "{% extends \"root.html\" %} {% block head %}")
|
||||
(title
|
||||
(text "{{ profile.username }} (warning) - {{ config.name }}"))
|
||||
|
||||
(text "{% endblock %} {% block body %} {{ macros::nav() }}")
|
||||
(main
|
||||
("class" "flex flex-col gap-2")
|
||||
(div
|
||||
("class" "card-nest")
|
||||
(div
|
||||
("class" "card small flex items-center justify-between gap-2")
|
||||
(div
|
||||
("class" "flex items-center gap-2")
|
||||
(text "{{ components::avatar(username=profile.username, size=\"24px\") }}")
|
||||
(span
|
||||
(text "{{ profile.username }}")))
|
||||
(b
|
||||
("class" "notification chip")
|
||||
(text "{{ text \"auth:label.before_you_view\" }}")))
|
||||
(div
|
||||
("class" "card flex flex-col gap-2")
|
||||
(span
|
||||
("class" "no_p_margin")
|
||||
(text "{{ profile.settings.warning|markdown|safe }}"))
|
||||
(div
|
||||
("class" "card w-full secondary flex gap-2")
|
||||
(button
|
||||
("onclick" "trigger('warnings::accept', ['{{ profile.id }}', '{{ warning_hash }}'])")
|
||||
(text "{{ icon \"check\" }}")
|
||||
(span
|
||||
(text "{{ text \"dialog:action.continue\" }}")))
|
||||
(a
|
||||
("href" "/")
|
||||
("class" "button red quaternary")
|
||||
(text "{{ icon \"x\" }}")
|
||||
(span
|
||||
(text "{{ text \"general:action.back\" }}")))))))
|
||||
|
||||
(text "{% endblock %}")
|
Loading…
Add table
Add a link
Reference in a new issue