add: profile and full search

This commit is contained in:
trisua 2025-05-18 16:43:56 -04:00
parent b8b0ef7f21
commit 3e4ee8126a
52 changed files with 897 additions and 484 deletions

8
Cargo.lock generated
View file

@ -3282,7 +3282,7 @@ dependencies = [
[[package]]
name = "tetratto"
version = "3.1.0"
version = "4.0.0"
dependencies = [
"ammonia",
"async-stripe",
@ -3313,7 +3313,7 @@ dependencies = [
[[package]]
name = "tetratto-core"
version = "3.1.0"
version = "4.0.0"
dependencies = [
"async-recursion",
"base16ct",
@ -3337,7 +3337,7 @@ dependencies = [
[[package]]
name = "tetratto-l10n"
version = "3.1.0"
version = "4.0.0"
dependencies = [
"pathbufd",
"serde",
@ -3346,7 +3346,7 @@ dependencies = [
[[package]]
name = "tetratto-shared"
version = "3.1.0"
version = "4.0.0"
dependencies = [
"ammonia",
"chrono",

View file

@ -1,6 +1,6 @@
[package]
name = "tetratto"
version = "3.1.0"
version = "4.0.0"
edition = "2024"
[features]

View file

@ -87,6 +87,7 @@ pub const TIMELINES_FOLLOWING_QUESTIONS: &str =
include_str!("./public/html/timelines/following_questions.html");
pub const TIMELINES_ALL_QUESTIONS: &str =
include_str!("./public/html/timelines/all_questions.html");
pub const TIMELINES_SEARCH: &str = include_str!("./public/html/timelines/search.html");
pub const MOD_AUDIT_LOG: &str = include_str!("./public/html/mod/audit_log.html");
pub const MOD_REPORTS: &str = include_str!("./public/html/mod/reports.html");
@ -283,6 +284,7 @@ pub(crate) async fn write_assets(config: &Config) -> PathBufD {
write_template!(html_path->"timelines/popular_questions.html"(crate::assets::TIMELINES_POPULAR_QUESTIONS) --config=config);
write_template!(html_path->"timelines/following_questions.html"(crate::assets::TIMELINES_FOLLOWING_QUESTIONS) --config=config);
write_template!(html_path->"timelines/all_questions.html"(crate::assets::TIMELINES_ALL_QUESTIONS) --config=config);
write_template!(html_path->"timelines/search.html"(crate::assets::TIMELINES_SEARCH) --config=config);
write_template!(html_path->"mod/audit_log.html"(crate::assets::MOD_AUDIT_LOG) -d "mod" --config=config);
write_template!(html_path->"mod/reports.html"(crate::assets::MOD_REPORTS) --config=config);

View file

@ -15,6 +15,7 @@ version = "1.0.0"
"general:link.reports" = "Reports"
"general:link.ip_bans" = "IP bans"
"general:link.stats" = "Stats"
"general:link.search" = "Search"
"general:action.save" = "Save"
"general:action.delete" = "Delete"
"general:action.purge" = "Purge"
@ -109,7 +110,6 @@ version = "1.0.0"
"communities:label.repost" = "Repost"
"communities:label.quote_post" = "Quote post"
"communities:label.expand_original" = "Expand original"
"communities:label.search" = "Search"
"communities:label.search_results" = "Search results"
"communities:label.query" = "Query"
"communities:label.join_new" = "Join new"

View file

@ -97,7 +97,7 @@ macro_rules! get_lang {
#[macro_export]
macro_rules! check_user_blocked_or_private {
($user:ident, $other_user:ident, $data:ident, $jar:ident) => {
($user:expr, $other_user:ident, $data:ident, $jar:ident) => {
// check require_account
if $user.is_none() && $other_user.settings.require_account {
return Err(Html(

View file

@ -755,6 +755,38 @@ select:focus {
border-bottom-right-radius: var(--radius);
}
.pillmenu:not(.rows) .row {
display: contents;
}
.pillmenu.rows {
flex-direction: column;
}
.pillmenu.rows .row {
display: flex;
}
.pillmenu.rows a {
border-radius: 0;
}
.pillmenu.rows .row:first-of-type a:first-child {
border-top-left-radius: var(--radius);
}
.pillmenu.rows .row:first-of-type a:last-child {
border-top-right-radius: var(--radius);
}
.pillmenu.rows .row:last-of-type a:first-child {
border-bottom-left-radius: var(--radius);
}
.pillmenu.rows .row:last-of-type a:last-child {
border-bottom-right-radius: var(--radius);
}
@media screen and (max-width: 900px) {
.pillmenu {
/* convert into a sidemenu */
@ -762,9 +794,9 @@ select:focus {
}
.pillmenu a:first-child {
border-bottom-left-radius: 0;
border-top-left-radius: var(--radius);
border-top-right-radius: var(--radius);
border-bottom-left-radius: 0;
}
.pillmenu a:last-child {
@ -772,6 +804,17 @@ select:focus {
border-bottom-left-radius: var(--radius);
border-bottom-right-radius: var(--radius);
}
.pillmenu.rows .row {
display: contents;
}
.pillmenu.rows .row:first-of-type a:first-child,
.pillmenu.rows .row:first-of-type a:last-child,
.pillmenu.rows .row:last-of-type a:first-child,
.pillmenu.rows .row:last-of-type a:last-child {
border-radius: 0;
}
}
/* notification */

View file

@ -72,4 +72,4 @@ config.connections.last_fm_key %}
}, 500);
}, 1000);
</script>
{% endif %} {% endblock %}
{%- endif %} {% endblock %}

View file

@ -7,15 +7,15 @@ hide_user_menu=true) }}
class="flex gap-2 items-center active"
onclick="toggle_sidebars(event)"
>
{{ icon "panel-left" }} {% if community %}
{{ icon "panel-left" }} {% if community -%}
<b class="name shorter">
{% if community.context.display_name %} {{
{% if community.context.display_name -%} {{
community.context.display_name }} {% else %} {{ community.title }}
{% endif %}
{%- endif %}
</b>
{% else %}
<b>{{ text "chats:label.my_chats" }}</b>
{% endif %}
{%- endif %}
</button>
</nav>
@ -27,36 +27,36 @@ hide_user_menu=true) }}
>
<a
href="/chats/0/0"
class="button quaternary channel_icon {% if selected_community == 0 %}selected{% endif %}"
class="button quaternary channel_icon {% if selected_community == 0 -%}selected{%- endif %}"
data-turbo="false"
>
{{ icon "message-circle" }}
</a>
{% for community in communities %} {% if community.id != 0 %}
{% for community in communities %} {% if community.id != 0 -%}
<a
href="/chats/{{ community.id }}/0"
class="button quaternary channel_icon {% if selected_community == community.id %}selected{% endif %}"
class="button quaternary channel_icon {% if selected_community == community.id -%}selected{%- endif %}"
data-turbo="false"
>
{{ components::community_avatar(id=community.id,
community=community, size="48px") }}
</a>
{% endif %} {% endfor %}
{%- endif %} {% endfor %}
</div>
<div class="sidebar flex flex-col gap-2 justify-between" id="channels_list">
<div class="flex flex-col gap-2 w-full">
<div class="title flex items-center justify-between channel_header">
{% if community %}
{% if community -%}
<b class="name shorter">
{% if community.context.display_name %} {{
{% if community.context.display_name -%} {{
community.context.display_name }} {% else %} {{
community.title }} {% endif %}
community.title }} {%- endif %}
</b>
{% else %}
<b>{{ text "chats:label.my_chats" }}</b>
{% endif %} {% if selected_community != 0 %}
{%- endif %} {% if selected_community != 0 -%}
<div class="dropdown">
<button
class="camo small"
@ -74,18 +74,18 @@ hide_user_menu=true) }}
}}</span
>
</a>
{% if can_manage_channels %}
{% if can_manage_channels -%}
<a href="/community/{{ selected_community }}/manage">
{{ icon "settings" }}
<span>{{ text "general:action.manage" }}</span>
</a>
{% endif %}
{%- endif %}
</div>
</div>
{% endif %}
{%- endif %}
</div>
{% if can_manage_channels %}
{% if can_manage_channels -%}
<a
class="button w-full justify-start quaternary"
href="/community/{{ selected_community }}/manage#/channels"
@ -93,7 +93,7 @@ hide_user_menu=true) }}
{{ icon "plus" }}
<span>{{ text "communities:action.create_channel" }}</span>
</a>
{% endif %}
{%- endif %}
<turbo-frame
id="channels_list_frame"
@ -105,7 +105,7 @@ hide_user_menu=true) }}
{{ components::user_plate(user=user, show_menu=true) }}
</div>
{% if channel %}
{% if channel -%}
<div class="w-full flex flex-col gap-2" id="stream" style="padding: 1rem">
<turbo-frame
id="stream_body_frame"
@ -132,7 +132,7 @@ hide_user_menu=true) }}
</button>
</form>
</div>
{% endif %}
{%- endif %}
<style>
:root {
@ -695,7 +695,7 @@ hide_user_menu=true) }}
};
</script>
{% if selected_channel %}
{% if selected_channel -%}
<script>
window.SUBSCRIBE_CHANNEL = "{{ selected_community }}" === "0";
@ -713,6 +713,6 @@ hide_user_menu=true) }}
}
}, 100);
</script>
{% endif %}
{%- endif %}
</div>
{% endblock %}

View file

@ -1,12 +1,12 @@
{%- import "components.html" as components -%}
<turbo-frame id="channels_list_frame">
<div
class="channels_list_half flex flex-col gap-2 {% if selected_community != 0 or selected_channel == 0%}no_members{% endif %}"
class="channels_list_half flex flex-col gap-2 {% if selected_community != 0 or selected_channel == 0%}no_members{%- endif -%}"
>
{% for channel in channels %}
<div class="flex flex-row gap-1">
<a
class="w-full justify-start button {% if selected_channel == channel.id %}quaternary{% else %}camo{% endif %}"
class="w-full justify-start button {% if selected_channel == channel.id -%}quaternary{% else %}camo{%- endif %}"
href="/chats/{{ selected_community }}/{{ channel.id }}"
data-turbo="{{ selected_community == '0' }}"
>
@ -16,7 +16,7 @@
<div class="dropdown">
<button
class="big_icon {% if selected_channel == channel.id %}quaternary{% else %}camo{% endif %}"
class="big_icon {% if selected_channel == channel.id -%}quaternary{% else %}camo{%- endif %}"
onclick="trigger('atto::hooks::dropdown', [event])"
exclude="dropdown"
style="width: 32px"
@ -25,7 +25,7 @@
</button>
<div class="inner">
{% if user.id == channel.owner %} {% if selected_community
{% if user.id == channel.owner -%} {% if selected_community
== 0 %}
<button
class="quaternary small"
@ -34,7 +34,7 @@
{{ icon "user-plus" }}
<span>{{ text "chats:action.add_someone" }}</span>
</button>
{% endif %}
{%- endif %}
<button
class="quaternary small"
@ -59,17 +59,17 @@
{{ icon "door-open" }}
<span>{{ text "chats:action.leave" }}</span>
</button>
{% endif %}
{%- endif %}
</div>
</div>
</div>
{% endfor %}
</div>
{% if selected_community == 0 and selected_channel %}
{% if selected_community == 0 and selected_channel -%}
<div class="members_list_half flex flex-col gap-2">
{% for member in members %} {{ components::user_plate(user=member,
show_kick=user.id == channel.owner) }} {% endfor %}
</div>
{% endif %}
{%- endif %}
</turbo-frame>

View file

@ -2,16 +2,16 @@
<turbo-frame id="stream_body_frame">
<!-- prettier-ignore -->
<div class="gap-2" id="stream_body">
{% if page != 0 %}
{% if page != 0 -%}
<div class="card flex gap-2 small tertiary flex-wrap">
<b>{{ text "chats:label.viewing_old_messages" }}</b>
<a href="/chats/{{ community }}/{{ channel }}/_stream?page={{ page - 1}}" class="button small" onclick="window.CURRENT_PAGE -= 1">
{{ text "chats:label.go_back" }}
</a>
</div>
{% endif %}
{%- endif %}
{% if message %}
{% if message -%}
<div class="card flex gap-2 small tertiary flex-wrap">
<b>{{ text "chats:label.viewing_single_message" }}</b>
<a href="/chats/{{ community }}/{{ channel }}?page={{ page }}" class="button small" onclick="window.VIEWING_SINGLE = false" target="_top">
@ -24,23 +24,23 @@
{% for message in messages %}
{{ components::message(user=message[1], message=message[0], grouped=message[2]) }}
{% endfor %}
{% endif %}
{%- endif %}
{% if messages|length > 0 %}
{% if messages|length > 0 -%}
<div class="flex gap-2 w-full justify-center">
<a class="button" href="/chats/{{ community }}/{{ channel }}/_stream?page={{ page + 1 }}" onclick="window.CURRENT_PAGE += 1">
{{ icon "clock" }}
<span>{{ text "chats:label.view_older" }}</span>
</a>
{% if page != 0 %}
{% if page != 0 -%}
<a class="button quaternary" href="/chats/{{ community }}/{{ channel }}/_stream?page={{ page - 1 }}" onclick="window.CURRENT_PAGE -= 1">
{{ icon "rewind" }}
<span>{{ text "chats:label.view_more_recent" }}</span>
</a>
{% endif %}
{%- endif %}
</div>
{% endif %}
{%- endif %}
</div>
<style>

View file

@ -51,13 +51,13 @@
class="title name shorter flex gap-2"
>
<!-- prettier-ignore -->
{% if community.context.display_name %}
{% if community.context.display_name -%}
{{ community.context.display_name }}
{% else %}
{{ community.title }}
{% endif %}
{%- endif %}
{% if community.context.is_nsfw %}
{% if community.context.is_nsfw -%}
<span
title="NSFW community"
class="flex items-center"
@ -65,10 +65,10 @@
>
{{ icon "square-asterisk" }}
</span>
{% endif %}
{%- endif %}
</h3>
{% if user %} {% if user.id != community.owner
{% if user -%} {% if user.id != community.owner
%}
<div class="dropdown">
<button
@ -92,16 +92,16 @@
</button>
</div>
</div>
{% endif %} {% endif %}
{%- endif %} {%- endif %}
</div>
<span class="fade">{{ community.title }}</span>
</div>
</div>
{% if user %}
{% if user -%}
<div class="card flex gap-2 flex-wrap" id="join_or_leave">
{% if not is_owner %} {% if not is_joined %} {% if not
{% if not is_owner -%} {% if not is_joined -%} {% if not
is_pending %}
<button class="primary" onclick="join_community()">
{{ icon "circle-plus" }}
@ -170,7 +170,7 @@
});
};
</script>
{% endif %} {% else %}
{%- endif %} {% else %}
<button
class="quaternary red"
onclick="leave_community()"
@ -187,7 +187,7 @@
<span>{{ text "communities:label.chats" }}</span>
</a>
{% if user and can_post %}
{% if user and can_post -%}
<a
href="/communities/intents/post?community={{ community.id }}"
class="button quaternary"
@ -196,7 +196,7 @@
{{ icon "plus" }}
<span>{{ text "general:action.post" }}</span>
</a>
{% endif %}
{%- endif %}
<script>
globalThis.leave_community = async () => {
@ -227,7 +227,7 @@
});
};
</script>
{% endif %} {% else %}
{%- endif %} {% else %}
<a
href="/chats/{{ community.id }}/0"
class="button quaternary"
@ -244,7 +244,8 @@
{{ icon "plus" }}
<span>{{ text "general:action.post" }}</span>
</a>
{% endif %} {% if can_manage_community or is_manager %}
{%- endif %} {% if can_manage_community or is_manager
-%}
<a
href="/community/{{ community.id }}/manage"
class="button primary"
@ -254,10 +255,10 @@
>{{ text "communities:action.configure" }}</span
>
</a>
{% endif %}
{%- endif %}
</div>
{% endif %}
{%- endif %}
</div>
<div class="card-nest flex flex-col">
@ -296,7 +297,7 @@
>{{ community.likes - community.dislikes
}}</b
>
{% if user %}
{% if user -%}
<div
class="flex gap-1 reactions_box"
hook="check_reactions"
@ -307,7 +308,7 @@
likes=community.likes,
dislikes=community.dislikes) }}
</div>
{% endif %}
{%- endif %}
</div>
</div>
</div>
@ -315,7 +316,7 @@
</div>
<div class="rhs w-full">
{% if can_read %} {% block content %}{% endblock %} {% else %}
{% if can_read -%} {% block content %}{% endblock %} {% else %}
<div class="card-nest">
<div class="card small flex items-center gap-2">
{{ icon "frown" }}
@ -331,7 +332,7 @@
</span>
</div>
</div>
{% endif %}
{%- endif %}
</div>
</div>
</div>

View file

@ -2,7 +2,7 @@
<title>Create post - {{ config.name }}</title>
{% endblock %} {% block body %} {{ macros::nav() }}
<main class="flex flex-col gap-2">
{% if drafts|length > 0 %}
{% if drafts|length > 0 -%}
<div class="pillmenu">
<a href="#/create" data-tab-button="create" class="active">
{{ icon "plus" }}
@ -14,7 +14,7 @@
<span>{{ text "communities:label.drafts" }}</span>
</a>
</div>
{% endif %}
{%- endif %}
<div class="card-nest" data-tab="create">
<div class="card small flex items-center justify-between gap-2">
@ -30,7 +30,7 @@
</div>
<div class="card tertiary flex flex-col gap-2">
{% if draft %}
{% if draft -%}
<div
class="card secondary w-full flex items-center justify-between gap-2 small"
>
@ -54,7 +54,7 @@
</button>
</div>
</div>
{% endif %}
{%- endif %}
<div class="card-nest">
<div class="card small flex flex-row gap-2 items-center">
@ -67,7 +67,7 @@
>
<option
value="{{ config.town_square }}"
selected="{% if not selected_community %}true{% else %}false{% endif %}"
selected="{% if not selected_community -%}true{% else %}false{%- endif %}"
>
{{ text "auth:link.my_profile" }}
</option>
@ -75,14 +75,14 @@
{% for community in communities %}
<option
value="{{ community.id }}"
selected="{% if selected_community == community.id %}true{% else %}false{% endif %}"
selected="{% if selected_community == community.id -%}true{% else %}false{%- endif %}"
>
<!-- prettier-ignore -->
{% if community.context.display_name %}
{% if community.context.display_name -%}
{{ community.context.display_name }}
{% else %}
{{ community.title }}
{% endif %}
{%- endif %}
</option>
{% endfor %}
</select>
@ -105,7 +105,7 @@
minlength="2"
maxlength="4096"
>
{% if draft %}{{ draft.content }}{% endif %}</textarea
{% if draft -%}{{ draft.content }}{%- endif %}</textarea
>
</div>
@ -114,9 +114,9 @@
<div class="flex justify-between gap-2">
<div class="flex gap-2">
{{ components::emoji_picker(element_id="content",
render_dialog=true) }} {% if is_supporter %} {{
render_dialog=true) }} {% if is_supporter -%} {{
components::file_picker(files_list_id="files_list")
}} {% endif %}
}} {%- endif %}
<button
class="small square quaternary"
@ -129,7 +129,7 @@
</div>
<div class="flex gap-2">
{% if draft %}
{% if draft -%}
<button
class="secondary small square"
title="Save as Draft"
@ -147,7 +147,7 @@
>
{{ icon "notepad-text-dashed" }}
</button>
{% endif %}
{%- endif %}
<button class="primary">
{{ text "communities:action.create" }}
@ -221,7 +221,7 @@
}
// remove draft
// {% if draft %}
// {% if draft -%}
if ("{{ draft.id }}") {
fetch("/api/v1/drafts/{{ draft.id }}", {
method: "DELETE",
@ -234,7 +234,7 @@
]);
});
}
// {% endif %}
// {%- endif %}
// ...
setTimeout(() => {
@ -325,7 +325,7 @@
</div>
</div>
{% if drafts|length > 0 %}
{% if drafts|length > 0 -%}
<div class="card-nest tertiary hidden" data-tab="drafts">
<div class="card small flex items-center gap-2">
{{ icon "notepad-text-dashed" }}
@ -384,7 +384,7 @@
});
}
</script>
{% endif %}
{%- endif %}
</main>
<script>

View file

@ -12,15 +12,15 @@
<div class="card flex flex-col gap-4">
<!-- prettier-ignore -->
{% for post in pinned %}
{% if post[0].context.repost and post[0].context.repost.reposting %}
{% if post[0].context.repost and post[0].context.repost.reposting -%}
{{ components::repost(repost=post[2], post=post[0], owner=post[1], secondary=true, show_community=false, can_manage_post=can_manage_posts) }}
{% else %}
{{ components::post(post=post[0], owner=post[1], question=post[3], secondary=true, show_community=false, can_manage_post=can_manage_posts) }}
{% endif %}
{%- endif %}
{% endfor %}
</div>
</div>
{% endif %}
{%- endif %}
<div class="card-nest">
<div class="card small flex gap-2 items-center">
@ -31,11 +31,11 @@
<div class="card flex flex-col gap-4">
<!-- prettier-ignore -->
{% for post in feed %}
{% if post[0].context.repost and post[0].context.repost.reposting %}
{% if post[0].context.repost and post[0].context.repost.reposting -%}
{{ components::repost(repost=post[2], post=post[0], owner=post[1], secondary=true, show_community=false, can_manage_post=can_manage_posts) }}
{% else %}
{{ components::post(post=post[0], owner=post[1], question=post[3], secondary=true, show_community=false, can_manage_post=can_manage_posts) }}
{% endif %}
{%- endif %}
{% endfor %}
{{ components::pagination(page=page, items=feed|length) }}

View file

@ -2,7 +2,7 @@
<title>My communities - {{ config.name }}</title>
{% endblock %} {% block body %} {{ macros::nav(selected="communities") }}
<main class="flex flex-col gap-2">
{% if user %}
{% if user -%}
<div class="card-nest">
<div class="card small">
<b>{{ text "communities:label.create_new" }}</b>
@ -31,8 +31,8 @@
</form>
</div>
{% if list|length >= 4 %} {{ components::supporter_ad(body="Become a
supporter to create up to 10 communities!") }} {% endif %} {% endif %}
{% if list|length >= 4 -%} {{ components::supporter_ad(body="Become a
supporter to create up to 10 communities!") }} {%- endif %} {%- endif %}
<div class="card-nest w-full">
<div class="card small flex items-center justify-between gap-2">

View file

@ -8,7 +8,7 @@
</div>
<div class="card flex flex-col gap-4">
{% if page == 0 %}
{% if page == 0 -%}
<div class="card-nest">
<div class="card small flex items-center gap-2">
{{ icon "crown" }}
@ -17,7 +17,7 @@
{{ components::user_card(user=owner) }}
</div>
{% endif %}
{%- endif %}
<!-- prettier-ignore -->
{% for item in list %}
@ -28,7 +28,7 @@
<span class="date">{{ item[0].created }}</span>
</span>
{% if can_manage_roles %}
{% if can_manage_roles -%}
<a
href="/community/{{ community.id }}/manage?uid={{ item[1].id }}#/members"
class="button small quaternary"
@ -36,7 +36,7 @@
{{ icon "pencil" }}
<span>{{ text "general:action.manage" }}</span>
</a>
{% endif %}
{%- endif %}
</div>
{{ components::user_card(user=item[1]) }}

View file

@ -37,7 +37,7 @@
<div class="flex gap-2">
{{ components::emoji_picker(element_id="content",
render_dialog=true) }} {% if is_supporter %} {{
render_dialog=true) }} {% if is_supporter -%} {{
components::file_picker(files_list_id="files_list") }} {% endif
%}
@ -47,7 +47,7 @@
</div>
</form>
</div>
{% endif %}
{%- endif %}
<div class="card-nest w-full" data-tab="replies">
<div class="card small flex items-center gap-2">

View file

@ -4,12 +4,12 @@
{{ macros::community_nav(community=community, selected="questions") }}
<!-- prettier-ignore -->
{% if user and can_post %}
{% if user and can_post -%}
<div style="display: contents">
{{ components::create_question_form(community=community.id,
is_global=true) }}
</div>
{% endif %}
{%- endif %}
<div class="card-nest">
<div class="card small flex gap-2 items-center">

View file

@ -5,7 +5,7 @@
<div class="card-nest">
<div class="card small flex items-center gap-2">
{{ icon "search" }}
<span>{{ text "communities:label.search" }}</span>
<span>{{ text "general:link.search" }}</span>
</div>
<form class="card flex flex-col gap-4">

View file

@ -18,17 +18,17 @@
<span>{{ text "communities:tab.members" }}</span>
</a>
{% if can_manage_channels %}
{% if can_manage_channels -%}
<a href="#/channels" data-tab-button="channels">
{{ icon "rss" }}
<span>{{ text "communities:tab.channels" }}</span>
</a>
{% endif %} {% if can_manage_emojis %}
{%- endif %} {% if can_manage_emojis -%}
<a href="#/emojis" data-tab-button="emojis">
{{ icon "smile" }}
<span>{{ text "communities:tab.emojis" }}</span>
</a>
{% endif %}
{%- endif %}
</div>
<div class="w-full flex flex-col gap-2" data-tab="general">
@ -42,13 +42,13 @@
<select onchange="save_access(event, 'read')">
<option
value="Everybody"
selected="{% if community.read_access == 'Everybody' %}true{% else %}false{% endif %}"
selected="{% if community.read_access == 'Everybody' -%}true{% else %}false{%- endif %}"
>
Everybody
</option>
<option
value="Joined"
selected="{% if community.read_access == 'Joined' %}true{% else %}false{% endif %}"
selected="{% if community.read_access == 'Joined' -%}true{% else %}false{%- endif %}"
>
Joined
</option>
@ -65,19 +65,19 @@
<select onchange="save_access(event, 'join')">
<option
value="Everybody"
selected="{% if community.join_access == 'Everybody' %}true{% else %}false{% endif %}"
selected="{% if community.join_access == 'Everybody' -%}true{% else %}false{%- endif %}"
>
Everybody
</option>
<option
value="Request"
selected="{% if community.join_access == 'Request' %}true{% else %}false{% endif %}"
selected="{% if community.join_access == 'Request' -%}true{% else %}false{%- endif %}"
>
Request
</option>
<option
value="Nobody"
selected="{% if community.join_access == 'Nobody' %}true{% else %}false{% endif %}"
selected="{% if community.join_access == 'Nobody' -%}true{% else %}false{%- endif %}"
>
Nobody
</option>
@ -94,19 +94,19 @@
<select onchange="save_access(event, 'write')">
<option
value="Everybody"
selected="{% if community.write_access == 'Everybody' %}true{% else %}false{% endif %}"
selected="{% if community.write_access == 'Everybody' -%}true{% else %}false{%- endif %}"
>
Everybody
</option>
<option
value="Joined"
selected="{% if community.write_access == 'Joined' %}true{% else %}false{% endif %}"
selected="{% if community.write_access == 'Joined' -%}true{% else %}false{%- endif %}"
>
Joined
</option>
<option
value="Owner"
selected="{% if community.write_access == 'Owner' %}true{% else %}false{% endif %}"
selected="{% if community.write_access == 'Owner' -%}true{% else %}false{%- endif %}"
>
Owner only
</option>
@ -267,7 +267,7 @@
<div class="card flex flex-col gap-2 w-full" id="membership_info"></div>
</div>
{% if can_manage_channels %}
{% if can_manage_channels -%}
<div
class="card tertiary w-full hidden flex flex-col gap-2"
data-tab="channels"
@ -441,7 +441,7 @@
});
}
</script>
{% endif %} {% if can_manage_emojis %}
{%- endif %} {% if can_manage_emojis -%}
<div
class="card tertiary w-full hidden flex flex-col gap-2"
data-tab="emojis"
@ -602,7 +602,7 @@
});
};
</script>
{% endif %}
{%- endif %}
</main>
<script>

View file

@ -8,7 +8,7 @@
style="--size: {{ size }}"
/>
{%- endmacro %} {% macro community_avatar(id, community=false, size="24px") -%}
{% if community %}
{% if community -%}
<img
src="/api/v1/communities/{{ id }}/avatar"
alt="{{ community.title }}'s avatar"
@ -24,7 +24,7 @@
loading="lazy"
style="--size: {{ size }}"
/>
{% endif %} {%- endmacro %} {% macro banner(username,
{%- endif %} {%- endmacro %} {% macro banner(username,
border_radius="var(--radius)") -%}
<img
title="{{ username }}'s banner"
@ -49,7 +49,7 @@ community %}
class="banner shadow"
loading="lazy"
/>
{% endif %} {%- endmacro %} {% macro community_listing_card(community) -%}
{%- endif %} {%- endmacro %} {% macro community_listing_card(community) -%}
<a
class="card secondary w-full flex items-center gap-4"
href="/community/{{ community.title }}"
@ -63,34 +63,34 @@ community %}
</a>
{%- endmacro %} {% macro username(user) -%}
<div style="display: contents">
{% if user.settings.display_name %} {{ user.settings.display_name }} {% else
%} {{ user.username }} {% endif %}
{% if user.settings.display_name -%} {{ user.settings.display_name }} {%
else %} {{ user.username }} {%- endif %}
</div>
{%- endmacro %} {% macro likes(id, asset_type, likes=0, dislikes=0,
secondary=false) -%}
<button
title="Like"
class="{% if secondary %}quaternary{% else %}camo{% endif %} small"
class="{% if secondary -%}quaternary{% else %}camo{%- endif %} small"
hook_element="reaction.like"
onclick="trigger('me::react', [event.target, '{{ id }}', '{{ asset_type }}', true])"
>
{{ icon "heart" }} {% if likes > 0 %}
{{ icon "heart" }} {% if likes > 0 -%}
<span>{{ likes }}</span>
{% endif %}
{%- endif %}
</button>
{% if not user or not user.settings.hide_dislikes %}
{% if not user or not user.settings.hide_dislikes -%}
<button
title="Dislike"
class="{% if secondary %}quaternary{% else %}camo{% endif %} small"
class="{% if secondary -%}quaternary{% else %}camo{%- endif %} small"
hook_element="reaction.dislike"
onclick="trigger('me::react', [event.target, '{{ id }}', '{{ asset_type }}', false])"
>
{{ icon "heart-crack" }} {% if dislikes > 0 %}
{{ icon "heart-crack" }} {% if dislikes > 0 -%}
<span>{{ dislikes }}</span>
{% endif %}
{%- endif %}
</button>
{% endif %} {%- endmacro %} {% macro full_username(user) -%}
{%- endif %} {%- endmacro %} {% macro full_username(user) -%}
<div class="flex items-center">
<a
href="/@{{ user.username }}"
@ -101,7 +101,7 @@ secondary=false) -%}
{{ self::username(user=user) }}
</a>
{{ self::online_indicator(user=user) }} {% if user.is_verified %}
{{ self::online_indicator(user=user) }} {% if user.is_verified -%}
<span
title="Verified"
style="color: var(--color-primary)"
@ -109,7 +109,7 @@ secondary=false) -%}
>
{{ icon "badge-check" }}
</span>
{% endif %}
{%- endif %}
</div>
{%- endmacro %} {% macro repost(repost, post, owner, secondary=false,
community=false, show_community=true, can_manage_post=false) -%}
@ -123,8 +123,8 @@ community=false, show_community=true, can_manage_post=false, repost=false,
expect_repost=false) -%} {% if community and show_community and community.id !=
config.town_square or question %}
<div class="card-nest">
{% if question %} {{ self::question(question=question[0], owner=question[1])
}} {% else %}
{% if question -%} {{ self::question(question=question[0],
owner=question[1]) }} {% else %}
<div class="card small">
<a
href="/api/v1/communities/find/{{ post.community }}"
@ -133,56 +133,56 @@ config.town_square or question %}
{{ self::community_avatar(id=post.community, community=community) }}
<b>
<!-- prettier-ignore -->
{% if community.context.display_name %}
{% if community.context.display_name -%}
{{ community.context.display_name }}
{% else %}
{{ community.title }}
{% endif %}
{%- endif %}
</b>
{% if post.context.is_pinned or post.context.is_profile_pinned %} {{
icon "pin" }} {% endif %}
{% if post.context.is_pinned or post.context.is_profile_pinned -%}
{{ icon "pin" }} {%- endif %}
</a>
</div>
{% endif %} {% endif %}
{%- endif %} {%- endif %}
<div
class="card flex flex-col gap-2 {% if secondary %}secondary{% endif %}"
class="card flex flex-col gap-2 {% if secondary -%}secondary{%- endif %}"
id="post:{{ post.id }}"
data-community="{{ post.community }}"
data-ownsup="{{ owner.permissions|has_supporter }}"
hook="verify_emojis"
>
<div class="w-full flex gap-2">
{% if not expect_repost %}
{% if not expect_repost -%}
<a href="/@{{ owner.username }}">
{{ self::avatar(username=owner.username, size="52px",
selector_type="username") }}
</a>
{% endif %}
{%- endif %}
<div
class="flex flex-col w-full gap-1 post_right {% if expect_repost %}repost{% endif %}"
class="flex flex-col w-full gap-1 post_right {% if expect_repost -%}repost{%- endif %}"
>
<div class="flex flex-wrap gap-2 items-center">
{% if expect_repost %}
{% if expect_repost -%}
<a href="/@{{ owner.username }}">
{{ self::avatar(username=owner.username, size="24px",
selector_type="username") }}
</a>
{% endif %}
{%- endif %}
<span class="name"
>{{ self::full_username(user=owner) }}</span
>
{% if post.context.edited != 0 %}
{% if post.context.edited != 0 -%}
<div class="flex">
<span class="fade date">{{ post.context.edited }}</span>
<sup title="Edited">*</sup>
</div>
{% else %}
<span class="fade date">{{ post.created }}</span>
{% endif %} {% if post.context.is_nsfw %}
{%- endif %} {% if post.context.is_nsfw -%}
<span
title="NSFW post"
class="flex items-center"
@ -190,7 +190,7 @@ config.town_square or question %}
>
{{ icon "square-asterisk" }}
</span>
{% endif %} {% if post.context.repost and
{%- endif %} {% if post.context.repost and
post.context.repost.reposting %}
<span
title="Repost"
@ -199,7 +199,7 @@ config.town_square or question %}
>
{{ icon "repeat-2" }}
</span>
{% endif %} {% if post.community == config.town_square %}
{%- endif %} {% if post.community == config.town_square -%}
<span
title="Posted to profile"
class="flex items-center"
@ -207,7 +207,7 @@ config.town_square or question %}
>
{{ icon "user-round" }}
</span>
{% endif %} {% if post.is_deleted %}
{%- endif %} {% if post.is_deleted -%}
<span
title="Deleted"
class="flex items-center"
@ -215,10 +215,10 @@ config.town_square or question %}
>
{{ icon "trash-2" }}
</span>
{% endif %}
{%- endif %}
</div>
{% if not post.context.content_warning %}
{% if not post.context.content_warning -%}
<!-- prettier-ignore -->
<span
id="post-content:{{ post.id }}"
@ -227,16 +227,16 @@ config.town_square or question %}
>
{{ post.content|markdown|safe }}
{% if expect_repost %}
{% if repost %}
{% if expect_repost -%}
{% if repost -%}
{{ self::post(post=repost[1], owner=repost[0], secondary=not secondary, community=false, show_community=false, can_manage_post=false) }}
{% else %}
<div class="card tertiary red flex items-center gap-2">
{{ icon "frown" }}
<span>Could not find original post...</span>
</div>
{% endif %}
{% endif %}
{%- endif %}
{%- endif %}
</span>
{{ self::post_media(upload_ids=post.uploads) }} {% else %}
@ -257,22 +257,22 @@ config.town_square or question %}
>
{{ post.content|markdown|safe }}
{% if expect_repost %}
{% if repost %}
{% if expect_repost -%}
{% if repost -%}
{{ self::post(post=repost[1], owner=repost[0], secondary=not secondary, community=false, show_community=false, can_manage_post=false) }}
{% else %}
<div class="card tertiary red flex items-center gap-2">
{{ icon "frown" }}
<span>Could not find original post...</span>
</div>
{% endif %}
{% endif %}
{%- endif %}
{%- endif %}
</span>
{{ self::post_media(upload_ids=post.uploads) }}
</div>
</details>
{% endif %}
{%- endif %}
<div class="flex flex-wrap gap-2 fade">
{% for tag in post.context.tags %}
@ -287,20 +287,20 @@ config.town_square or question %}
</div>
<div class="flex justify-between items-center gap-2 w-full">
{% if user %}
{% if user -%}
<div
class="flex gap-1 reactions_box"
hook="check_reactions"
hook-arg:id="{{ post.id }}"
>
<!-- prettier-ignore -->
{% if post.context.reactions_enabled %}
{% if post.content|length > 0 %}
{% if post.context.reactions_enabled -%}
{% if post.content|length > 0 -%}
{{ self::likes(id=post.id, asset_type="Post", likes=post.likes, dislikes=post.dislikes) }}
{% endif %}
{% endif %}
{%- endif %}
{%- endif %}
{% if post.context.repost and post.context.repost.reposting %}
{% if post.context.repost and post.context.repost.reposting -%}
<a
href="/post/{{ post.context.repost.reposting }}"
class="button small camo"
@ -308,11 +308,11 @@ config.town_square or question %}
>
{{ icon "expand" }}
</a>
{% endif %}
{%- endif %}
</div>
{% else %}
<div></div>
{% endif %}
{%- endif %}
<div class="flex gap-1 buttons_box">
<a href="/post/{{ post.id }}" class="button camo small">
@ -328,7 +328,7 @@ config.town_square or question %}
{{ icon "external-link" }}
</a>
{% if user %}
{% if user -%}
<div class="dropdown">
<button
class="camo small"
@ -357,7 +357,7 @@ config.town_square or question %}
>{{ text "communities:label.quote_post" }}</span
>
</button>
{% endif %} {% if user.id != post.owner %}
{%- endif %} {% if user.id != post.owner -%}
<b class="title">{{ text "general:label.safety" }}</b>
<button
class="red"
@ -366,10 +366,10 @@ config.town_square or question %}
{{ icon "flag" }}
<span>{{ text "general:action.report" }}</span>
</button>
{% endif %} {% if (user.id == post.owner) or is_helper
{%- endif %} {% if (user.id == post.owner) or is_helper
or can_manage_post %}
<b class="title">{{ text "general:action.manage" }}</b>
{% if user.id == post.owner %}
{% if user.id == post.owner -%}
<a href="/post/{{ post.id }}#/edit">
{{ icon "pen" }}
<span
@ -377,7 +377,7 @@ config.town_square or question %}
}}</span
>
</a>
{% endif %}
{%- endif %}
<a href="/post/{{ post.id }}#/configure">
{{ icon "settings" }}
@ -386,7 +386,7 @@ config.town_square or question %}
>
</a>
{% if not post.is_deleted %}
{% if not post.is_deleted -%}
<button
class="red"
onclick="trigger('me::remove_post', ['{{ post.id }}'])"
@ -394,7 +394,7 @@ config.town_square or question %}
{{ icon "trash" }}
<span>{{ text "general:action.delete" }}</span>
</button>
{% endif %} {% if is_helper and post.is_deleted %}
{%- endif %} {% if is_helper and post.is_deleted -%}
<button
class="red"
onclick="trigger('me::purge_post', ['{{ post.id }}'])"
@ -410,17 +410,17 @@ config.town_square or question %}
{{ icon "undo" }}
<span>{{ text "general:action.restore" }}</span>
</button>
{% endif %} {% endif %}
{%- endif %} {%- endif %}
</div>
</div>
{% endif %}
{%- endif %}
</div>
</div>
</div>
{% if community and show_community and community.id != config.town_square or
question %}
</div>
{% endif %} {%- endmacro %} {% macro post_media(upload_ids) -%} {% if
{%- endif %} {%- endmacro %} {% macro post_media(upload_ids) -%} {% if
upload_ids|length > 0%}
<div class="media_gallery gap-2">
{% for upload in upload_ids %}
@ -431,10 +431,10 @@ upload_ids|length > 0%}
/>
{% endfor %}
</div>
{% endif %} {%- 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 %}
{% if not notification.read -%}
<svg
width="24"
height="24"
@ -443,7 +443,7 @@ upload_ids|length > 0%}
>
<circle cx="12" cy="12" r="6"></circle>
</svg>
{% endif %}
{%- endif %}
<b class="no_p_margin">{{ notification.title|markdown|safe }}</b>
</div>
@ -453,7 +453,7 @@ upload_ids|length > 0%}
>
<div class="card secondary w-full flex flex-wrap gap-2">
{% if notification.read %}
{% if notification.read -%}
<button
class="tertiary"
onclick="trigger('me::update_notification_read_status', ['{{ notification.id }}', false])"
@ -469,7 +469,7 @@ upload_ids|length > 0%}
{{ icon "check" }}
<span>{{ text "notifs:action.mark_as_read" }}</span>
</button>
{% endif %}
{%- endif %}
<button
class="red tertiary"
@ -497,7 +497,7 @@ upload_ids|length > 0%}
</a>
{%- endmacro %} {% macro pagination(page=0, items=0, key="", value="") -%}
<div class="flex justify-between gap-2 w-full">
{% if page > 0 %}
{% if page > 0 -%}
<a
class="button quaternary"
href="?page={{ page - 1 }}{{ key }}{{ value }}"
@ -507,7 +507,7 @@ upload_ids|length > 0%}
</a>
{% else %}
<div></div>
{% endif %} {% if items != 0 %}
{%- endif %} {% if items != 0 -%}
<a
class="button quaternary"
href="?page={{ page + 1 }}{{ key }}{{ value }}"
@ -515,7 +515,7 @@ upload_ids|length > 0%}
<span>{{ text "general:link.next" }}</span>
{{ icon "arrow-right"}}
</a>
{% endif %}
{%- endif %}
</div>
{%- endmacro %} {% macro online_indicator(user) -%} {% if not
user.settings.private_last_seen or is_helper %}
@ -569,26 +569,26 @@ user.settings.private_last_seen or is_helper %}
<circle cx="12" cy="12" r="6"></circle>
</svg>
</div>
{% endif %} {%- endmacro %} {% macro theme(user, theme_preference) -%} {% if
user %} {% if user.settings.theme_hue %}
{%- endif %} {%- endmacro %} {% macro theme(user, theme_preference) -%} {% if
user %} {% if user.settings.theme_hue -%}
<style>
:root, * {
--hue: {{ user.settings.theme_hue }} !important;
}
</style>
{% endif %} {% if user.settings.theme_sat %}
{%- endif %} {% if user.settings.theme_sat -%}
<style>
:root, * {
--sat: {{ user.settings.theme_sat }} !important;
}
</style>
{% endif %} {% if user.settings.theme_lit %}
{%- endif %} {% if user.settings.theme_lit -%}
<style>
:root, * {
--lit: {{ user.settings.theme_lit }} !important;
}
</style>
{% endif %} {% if theme_preference %}
{%- endif %} {% if theme_preference -%}
<script>
function match_user_theme() {
const pref = "{{ theme_preference }}".toLowerCase();
@ -604,7 +604,7 @@ user %} {% if user.settings.theme_hue %}
match_user_theme();
}, 150);
</script>
{% endif %}
{%- endif %}
<!-- prettier-ignore -->
<div style="display: none;">
{{ self::theme_color(color=user.settings.theme_color_surface, css="color-surface") }}
@ -627,11 +627,12 @@ user %} {% if user.settings.theme_hue %}
{{ self::theme_color(color=user.settings.theme_color_text_secondary, css="color-text-secondary") }}
{{ self::theme_color(color=user.settings.theme_color_secondary_lowered, css="color-secondary-lowered") }}
{% if user.permissions|has_supporter %}
{% if user.permissions|has_supporter -%}
<style>{{ user.settings.theme_custom_css }}</style>
{% endif %}
{%- endif %}
</div>
{% endif %} {%- endmacro %} {% macro theme_color(color, css) -%} {% if color %}
{%- endif %} {%- endmacro %} {% macro theme_color(color, css) -%} {% if color
-%}
<!-- prettier-ignore -->
<style>
:root,
@ -639,7 +640,7 @@ user %} {% if user.settings.theme_hue %}
--{{ css }}: {{ color|color }} !important;
}
</style>
{% endif %} {%- endmacro %} {% macro quote_form() -%} {% if config.town_square
{%- endif %} {%- endmacro %} {% macro quote_form() -%} {% if config.town_square
and user %}
<div class="card-nest">
<div class="card small flex flex-col">
@ -681,12 +682,12 @@ and user %}
]);
}
</script>
{% endif %} {%- endmacro %} {% macro question(question, owner,
{%- endif %} {%- endmacro %} {% macro question(question, owner,
show_community=true, secondary=false) -%}
<div class="card{% if secondary %} secondary{% endif %} flex gap-2">
{% if owner.id == 0 %}
<div class="card{% if secondary -%} secondary{%- endif %} flex gap-2">
{% if owner.id == 0 -%}
<span>
{% if profile and profile.settings.anonymous_avatar_url %}
{% if profile and profile.settings.anonymous_avatar_url -%}
<img
src="/api/v1/util/proxy?url={{ profile.settings.anonymous_avatar_url }}"
alt="anonymous' avatar"
@ -695,21 +696,21 @@ show_community=true, secondary=false) -%}
style="--size: 52px"
/>
{% else %} {{ self::avatar(username=owner.username,
selector_type="username", size="52px") }} {% endif %}
selector_type="username", size="52px") }} {%- endif %}
</span>
{% else %}
<a href="/@{{ owner.username }}">
{{ self::avatar(username=owner.username, selector_type="username",
size="52px") }}
</a>
{% endif %}
{%- endif %}
<div class="flex flex-col gap-1">
<div class="flex items-center gap-2 flex-wrap">
<!-- prettier-ignore -->
<span class="name">
{% if owner.id == 0 %}
{% if profile and profile.settings.anonymous_username %}
{% if owner.id == 0 -%}
{% if profile and profile.settings.anonymous_username -%}
<span class="flex items-center gap-2">
<b>{{ profile.settings.anonymous_username }}</b>
<span
@ -722,10 +723,10 @@ show_community=true, secondary=false) -%}
</span>
{% else %}
<b>anonymous</b>
{% endif %}
{%- endif %}
{% else %}
{{ self::full_username(user=owner) }}
{% endif %}
{%- endif %}
</span>
<span class="date">{{ question.created }}</span>
@ -738,7 +739,7 @@ show_community=true, secondary=false) -%}
{{ icon "message-circle-heart" }}
</span>
{% if question.context.is_nsfw %}
{% if question.context.is_nsfw -%}
<span
title="NSFW community"
class="flex items-center"
@ -746,18 +747,18 @@ show_community=true, secondary=false) -%}
>
{{ icon "square-asterisk" }}
</span>
{% endif %} {% if question.community > 0 and show_community %}
{%- endif %} {% if question.community > 0 and show_community -%}
<a
href="/api/v1/communities/find/{{ question.community }}"
class="flex items-center"
>
{{ self::community_avatar(id=question.community, size="24px") }}
</a>
{% endif %} {% if question.is_global %}
{%- endif %} {% if question.is_global -%}
<a class="notification chip" href="/question/{{ question.id }}"
>{{ question.answer_count }} answers</a
>
{% endif %}
{%- endif %}
</div>
<span class="no_p_margin" style="font-weight: 500"
@ -774,11 +775,11 @@ header="", is_global=false) -%}
{{ icon "message-circle-heart" }}
<span class="no_p_margin">
<!-- prettier-ignore -->
{% if header %}
{% if header -%}
{{ header|markdown|safe }}
{% else %}
{{ text "requests:label.ask_question" }}
{% endif %}
{%- endif %}
</span>
</div>
@ -839,7 +840,7 @@ secondary=false, show_community=true) -%}
show_community=show_community) }}
<div
class="small card flex justify-between flex-wrap gap-2{% if secondary %} secondary{% endif %}"
class="small card flex justify-between flex-wrap gap-2{% if secondary -%} secondary{%- endif %}"
>
<div
class="flex gap-1 reactions_box"
@ -853,14 +854,14 @@ secondary=false, show_community=true) -%}
<div class="flex gap-1 buttons_box">
<a href="/question/{{ question[0].id }}" class="button small">
{{ icon "external-link" }} {% if user %}
{{ icon "external-link" }} {% if user -%}
<span>{{ text "requests:label.answer" }}</span>
{% else %}
<span>{{ text "general:action.open" }}</span>
{% endif %}
{%- endif %}
</a>
{% if user %} {% if can_manage_questions or is_helper or
{% if user -%} {% if can_manage_questions or is_helper or
question[1].id == user.id %}
<div class="dropdown">
<button
@ -881,7 +882,7 @@ secondary=false, show_community=true) -%}
</button>
</div>
</div>
{% endif %} {% endif %}
{%- endif %} {%- endif %}
</div>
</div>
</div>
@ -931,7 +932,7 @@ state.data %}
</div>
</div>
</div>
{% endif %} {%- endmacro %} {% macro last_fm_playing(state, size="60px") -%} {%
{%- endif %} {%- endmacro %} {% macro last_fm_playing(state, size="60px") -%} {%
if state and state.data %}
<div class="card-nest">
<div class="card flex items-center justify-between gap-2 small">
@ -967,7 +968,7 @@ if state and state.data %}
></span
>
{% if state.data.duration_ms and state.data.duration_ms != "0" %}
{% if state.data.duration_ms and state.data.duration_ms != "0" -%}
<span
hook="spotify_time_text"
hook-arg:updated="{{ state.data.timestamp }}"
@ -975,22 +976,22 @@ if state and state.data %}
hook-arg:duration="{{ state.data.duration_ms }}"
hook-arg:display="full"
></span>
{% endif %}
{%- endif %}
</div>
</div>
</div>
{% endif %} {%- endmacro %} {% macro connection_icon(key) -%}
{%- endif %} {%- endmacro %} {% macro connection_icon(key) -%}
<!-- prettier-ignore -->
<div style="display: contents;">
{% if key == "Spotify" %}
{% if key == "Spotify" -%}
{{ icon "spotify" }}
{% elif key == "LastFm" %}
{{ icon "last_fm" }}
{% endif %}
{%- endif %}
</div>
{%- endmacro %} {% macro connection_url(key, value) -%} {% if value[0].data.url
%} {{ value[0].data.url }} {% elif key == "LastFm" %} https://last.fm/user/{{
value[0].data.name }} {% endif %} {%- endmacro %} {% macro
value[0].data.name }} {%- endif %} {%- endmacro %} {% macro
message_actions(can_manage_message, owner, message, owner) -%}
<div class="dropdown">
<button
@ -1002,12 +1003,12 @@ message_actions(can_manage_message, owner, message, owner) -%}
</button>
<div class="inner">
{% if can_manage_message or (user and user.id == message.owner) %}
{% if can_manage_message or (user and user.id == message.owner) -%}
<button class="red" onclick="delete_message('{{ message.id }}')">
{{ icon "trash" }}
<span>{{ text "general:action.delete" }}</span>
</button>
{% endif %}
{%- endif %}
<button
onclick="window.location.href = `${window.location.origin}/chats/{{ community }}/{{ channel }}?message={{ message.id }}`"
@ -1032,17 +1033,17 @@ message_actions(can_manage_message, owner, message, owner) -%}
{%- endmacro %} {% macro message(user, message, can_manage_message=false,
grouped=false) -%}
<div
class="card secondary message flex gap-2 {% if grouped %}grouped{% endif %}"
class="card secondary message flex gap-2 {% if grouped -%}grouped{%- endif %}"
id="message-{{ message.id }}"
>
{% if not grouped %}
{% if not grouped -%}
<a href="/@{{ user.username }}" target="_top">
{{ self::avatar(username=user.username, size="42px") }}
</a>
{% endif %}
{%- endif %}
<div class="flex flex-col gap-1 w-full">
{% if not grouped %}
{% if not grouped -%}
<div class="flex gap-2 w-full justify-between flex-wrap">
<div class="flex gap-2">
{{ self::full_username(user=user) }} {% if message.edited !=
@ -1052,7 +1053,7 @@ grouped=false) -%}
>
{% else %}
<span class="date">{{ message.created }}</span>
{% endif %}
{%- endif %}
</div>
<div class="flex gap-2 hidden">
@ -1060,17 +1061,17 @@ grouped=false) -%}
can_manage_message=can_manage_message) }}
</div>
</div>
{% endif %}
{%- endif %}
<div class="flex w-full gap-2 justify-between">
<span class="no_p_margin">{{ message.content|markdown|safe }}</span>
{% if grouped %}
{% if grouped -%}
<div class="hidden">
{{ self::message_actions(owner=user, message=message,
can_manage_message=can_manage_message) }}
</div>
{% endif %}
{%- endif %}
</div>
</div>
</div>
@ -1087,7 +1088,7 @@ grouped=false) -%}
<span>{{ text "auth:link.settings" }}</span>
</a>
{% if is_helper %}
{% if is_helper -%}
<b class="title">{{ text "general:label.mod" }}</b>
<a href="/mod_panel/audit_log">
@ -1109,7 +1110,7 @@ grouped=false) -%}
{{ icon "chart-line" }}
<span>{{ text "general:link.stats" }}</span>
</a>
{% endif %}
{%- endif %}
<b class="title">{{ config.name }}</b>
@ -1166,10 +1167,10 @@ other_user.connections.Spotify[1].data.track %}
}}</span
>
</div>
{% endif %} {%- endmacro %} {% macro user_plate(user, show_menu=false,
{%- endif %} {%- endmacro %} {% macro user_plate(user, show_menu=false,
show_kick=false, secondary=false) -%}
<div
class="flex gap-2 items-center card tiny user_plate {% if secondary %}secondary{% endif %}"
class="flex gap-2 items-center card tiny user_plate {% if secondary -%}secondary{%- endif %}"
>
<a href="/@{{ user.username }}">
{{ self::avatar(username=user.username, size="42px",
@ -1178,13 +1179,13 @@ show_kick=false, secondary=false) -%}
<div
class="flex justify-center flex-col"
style="{% if show_menu or show_kick %}width: 60%{% else %}max-width: 200px{% endif %}"
style="{% if show_menu or show_kick -%}width: 60%{% else %}max-width: 200px{%- endif %}"
>
{{ self::full_username(user=user) }}
<div class="user_status">{{ self::user_status(other_user=user) }}</div>
</div>
{% if show_menu %}
{% if show_menu -%}
<div class="dropdown">
<button
class="camo small square"
@ -1216,7 +1217,7 @@ show_kick=false, secondary=false) -%}
</button>
</div>
</div>
{% endif %}
{%- endif %}
</div>
{%- endmacro %} {% macro emoji_picker(element_id, render_dialog=false) -%}
<button
@ -1228,7 +1229,7 @@ show_kick=false, secondary=false) -%}
{{ icon "smile-plus" }}
</button>
{% if render_dialog %}
{% if render_dialog -%}
<dialog id="emoji_dialog">
<div class="inner flex flex-col gap-2">
<script
@ -1307,7 +1308,7 @@ show_kick=false, secondary=false) -%}
</div>
</div>
</dialog>
{% endif %} {%- endmacro %} {% macro file_picker(files_list_id) -%}
{%- endif %} {%- endmacro %} {% macro file_picker(files_list_id) -%}
<button
class="button small square quaternary"
onclick="pick_file()"
@ -1384,11 +1385,11 @@ is_supporter %}
onclick="window.location.href = '/settings#/account/billing'"
>
<div class="card w-full flex flex-wrap items-center gap-2 justify-between">
{% if body %}
{% if body -%}
<b>{{ body }}</b>
{% else %}
<b>{{ text "general:label.supporter_motivation" }}</b>
{% endif %}
{%- endif %}
<a href="/settings#/account/billing" class="button small">
{{ icon "heart" }}
@ -1396,4 +1397,4 @@ is_supporter %}
</a>
</div>
</div>
{% endif %} {%- endmacro %}
{%- endif %} {%- endmacro %}

View file

@ -6,30 +6,30 @@
<b>{{ config.name }}</b>
</a>
{% if show_lhs %}
{% if show_lhs -%}
<a
href="{{ home }}"
class="button {% if selected == 'home' %}active{% endif %}"
class="button {% if selected == 'home' -%}active{%- endif %}"
>
{{ icon "house" }}
<span class="desktop">{{ text "general:link.home" }}</span>
</a>
{% if user %}
{% if user -%}
<a
href="/communities"
class="button {% if selected == 'communities' %}active{% endif %}"
class="button {% if selected == 'communities' -%}active{%- endif %}"
>
{{ icon "book-heart" }}
<span class="desktop"
>{{ text "general:link.communities" }}</span
>
</a>
{% endif %} {% endif %}
{%- endif %} {%- endif %}
</div>
<div class="flex nav_side">
{% if user %}
{% if user -%}
<a
href="/communities/intents/post"
class="button"
@ -40,7 +40,7 @@
<a
href="/chats/0/0"
class="button {% if selected == 'chats' %}active{% endif %}"
class="button {% if selected == 'chats' -%}active{%- endif %}"
title="Chats"
>
{{ icon "message-circle" }}
@ -48,12 +48,12 @@
<a
href="/requests"
class="button {% if selected == 'requests' %}active{% endif %}"
class="button {% if selected == 'requests' -%}active{%- endif %}"
title="Requests"
>
{{ icon "inbox" }}
<span
class="notification tr {% if user.request_count <= 0 %}hidden{% endif %}"
class="notification tr {% if user.request_count <= 0 -%}hidden{%- endif %}"
id="requests_span"
>{{ user.request_count }}</span
>
@ -61,18 +61,18 @@
<a
href="/notifs"
class="button {% if selected == 'notifications' %}active{% endif %}"
class="button {% if selected == 'notifications' -%}active{%- endif %}"
title="Notifications"
>
{{ icon "bell" }}
<span
class="notification tr {% if user.notification_count <= 0 %}hidden{% endif %}"
class="notification tr {% if user.notification_count <= 0 -%}hidden{%- endif %}"
id="notifications_span"
>{{ user.notification_count }}</span
>
</a>
{% if not hide_user_menu %}
{% if not hide_user_menu -%}
<div class="dropdown">
<button
class="flex-row title"
@ -86,7 +86,7 @@
{{ components::user_menu() }}
</div>
{% endif %} {% else %}
{%- endif %} {% else %}
<div class="dropdown">
<button
class="title"
@ -113,71 +113,92 @@
</a>
</div>
</div>
{% endif %}
{%- endif %}
</div>
</div>
</nav>
{%- endmacro %} {% macro timelines_nav(selected="") -%}
<div class="pillmenu w-full">
{% if user %}
<a href="/" class="{% if selected == 'home' %}active{% endif %}">
<div class="pillmenu {% if user -%}rows{% endif %} w-full">
<div class="row">
{% if user -%}
<a href="/" class="{% if selected == 'home' -%}active{%- endif %}">
{{ icon "newspaper" }}
<span>{{ text "general:link.home" }}</span>
</a>
<a href="/stacks" class="{% if selected == 'stacks' %}active{% endif %}">
<a
href="/stacks"
class="{% if selected == 'stacks' -%}active{%- endif %}"
>
{{ icon "layers" }}
<span>{{ text "stacks:link.stacks" }}</span>
</a>
{% else %}
<a href="/" class="{% if selected == 'all' %}active{% endif %}">
<a href="/" class="{% if selected == 'all' -%}active{%- endif %}">
{{ icon "earth" }}
<span>{{ text "general:link.all" }}</span>
</a>
{% endif %}
{%- endif %}
<a href="/popular" class="{% if selected == 'popular' %}active{% endif %}">
<a
href="/popular"
class="{% if selected == 'popular' -%}active{%- endif %}"
>
{{ icon "trending-up" }}
<span>{{ text "general:link.popular" }}</span>
</a>
</div>
{% if user %}
<div class="row">
{% if user -%}
<a
href="/following"
class="{% if selected == 'following' %}active{% endif %}"
class="{% if selected == 'following' -%}active{%- endif %}"
>
{{ icon "rss" }}
<span>{{ text "general:link.following" }}</span>
</a>
<a href="/all" class="{% if selected == 'all' %}active{% endif %}">
<a
href="/search"
class="{% if selected == 'search' -%}active{%- endif %}"
>
{{ icon "search" }}
<span>{{ text "general:link.search" }}</span>
</a>
<a href="/all" class="{% if selected == 'all' -%}active{%- endif %}">
{{ icon "earth" }}
<span>{{ text "general:link.all" }}</span>
</a>
{% endif %}
{%- endif %}
</div>
</div>
{%- endmacro %} {% macro timelines_secondary_nav(posts="", questions="",
selected="posts") -%} {% if user %}
selected="posts") -%} {% if user -%}
<div class="pillmenu w-full">
<a href="{{ posts }}" class="{% if selected == 'posts' %}active{% endif %}">
<a
href="{{ posts }}"
class="{% if selected == 'posts' -%}active{%- endif %}"
>
{{ icon "newspaper" }}
<span>{{ text "communities:label.posts" }}</span>
</a>
<a
href="{{ questions }}"
class="{% if selected == 'questions' %}active{% endif %}"
class="{% if selected == 'questions' -%}active{%- endif %}"
>
{{ icon "message-circle-heart" }}
<span>{{ text "communities:label.questions" }}</span>
</a>
</div>
{% endif %} {%- endmacro %} {% macro community_nav(community, selected="") -%}
{% if community.context.enable_questions %}
{%- endif %} {%- endmacro %} {% macro community_nav(community, selected="") -%}
{% if community.context.enable_questions -%}
<div class="pillmenu">
<a
href="/community/{{ community.title }}"
class="{% if selected == 'posts' %}active{% endif %}"
class="{% if selected == 'posts' -%}active{%- endif %}"
>
{{ icon "newspaper" }}
<span>{{ text "communities:tab.posts" }}</span>
@ -185,10 +206,10 @@ selected="posts") -%} {% if user %}
<a
href="/community/{{ community.title }}/questions"
class="{% if selected == 'questions' %}active{% endif %}"
class="{% if selected == 'questions' -%}active{%- endif %}"
>
{{ icon "message-circle-heart" }}
<span>{{ text "communities:tab.questions" }}</span>
</a>
</div>
{% endif %} {%- endmacro %}
{%- endif %} {%- endmacro %}

View file

@ -9,6 +9,7 @@
<div class="card flex flex-col gap-4">
<span>{{ error_text }}</span>
<div class="w-full flex gap-2">
<a class="button primary" href="/">Home</a>
<a class="button secondary" href="javascript:history.back()"

View file

@ -86,7 +86,7 @@
</div>
</div>
</div>
{% endif %} {% endfor %} {% for question in questions %}
{%- endif %} {% endfor %} {% for question in questions %}
<!-- prettier-ignore -->
<div class="card-nest">
{{ components::question(question=question[0], owner=question[1]) }}
@ -112,9 +112,9 @@
<div class="flex flex-wrap w-full gap-2">
{{ components::emoji_picker(element_id="content",
render_dialog=true) }} {% if is_supporter %} {{
render_dialog=true) }} {% if is_supporter -%} {{
components::file_picker(files_list_id="files_list") }}
{% endif %}
{%- endif %}
<button class="primary">{{ text "requests:label.answer" }}</button>
<button type="button" class="red quaternary" onclick="trigger('me::remove_question', ['{{ question[0].id }}'])">{{ text "general:action.delete" }}</button>

View file

@ -38,7 +38,7 @@
<span>{{ text "settings:label.delete_account" }}</span>
</button>
{% if profile.permissions != 131073 %}
{% if profile.permissions != 131073 -%}
<button
class="red quaternary"
onclick="update_user_role(131073)"
@ -49,7 +49,7 @@
<button class="quaternary" onclick="update_user_role(1)">
Unban
</button>
{% endif %}
{%- endif %}
</div>
</div>

View file

@ -2,20 +2,20 @@
<title>Post quotes - {{ config.name }}</title>
{% endblock %} {% block body %} {{ macros::nav() }}
<main class="flex flex-col gap-2">
{% if post.replying_to %}
{% if post.replying_to -%}
<a href="/post/{{ post.replying_to }}" class="button">
{{ icon "arrow-up" }}
<span>{{ text "communities:action.continue_thread" }}</span>
</a>
{% endif %}
{%- endif %}
<!-- prettier-ignore -->
<div style="display: contents;">
{% if post.context.repost and post.context.repost.reposting %}
{% if post.context.repost and post.context.repost.reposting -%}
{{ components::repost(repost=reposting, post=post, owner=owner, community=community, show_community=true, can_manage_post=can_manage_posts) }}
{% else %}
{{ components::post(post=post, owner=owner, question=question, community=community, show_community=true, can_manage_post=can_manage_posts) }}
{% endif %}
{%- endif %}
</div>
<div class="pillmenu">
@ -35,14 +35,14 @@
</a>
</div>
{% if (user and user.id == post.owner) or can_manage_posts %}
{% if (user and user.id == post.owner) or can_manage_posts -%}
<div class="pillmenu">
{% if user and user.id == post.owner %}
{% if user and user.id == post.owner -%}
<a href="/post/{{ post.id }}#/edit">
{{ icon "pen" }}
<span>{{ text "communities:label.edit_content" }}</span>
</a>
{% endif %}
{%- endif %}
<a href="/post/{{ post.id }}/likes" class="active">
{{ icon "heart" }}
<span>{{ text "communities:label.likes" }}</span>
@ -53,7 +53,7 @@
<span>{{ text "communities:action.configure" }}</span>
</a>
</div>
{% endif %}
{%- endif %}
<div class="card-nest w-full">
<div class="card small flex items-center gap-2">

View file

@ -2,23 +2,23 @@
<title>Post - {{ config.name }}</title>
{% endblock %} {% block body %} {{ macros::nav() }}
<main class="flex flex-col gap-2">
{% if post.replying_to %}
{% if post.replying_to -%}
<a href="/post/{{ post.replying_to }}" class="button">
{{ icon "arrow-up" }}
<span>{{ text "communities:action.continue_thread" }}</span>
</a>
{% endif %}
{%- endif %}
<!-- prettier-ignore -->
<div style="display: contents;">
{% if post.context.repost and post.context.repost.reposting %}
{% if post.context.repost and post.context.repost.reposting -%}
{{ components::repost(repost=reposting, post=post, owner=owner, community=community, show_community=true, can_manage_post=can_manage_posts) }}
{% else %}
{{ components::post(post=post, owner=owner, question=question, community=community, show_community=true, can_manage_post=can_manage_posts) }}
{% endif %}
{%- endif %}
</div>
{% if user and post.context.comments_enabled %}
{% if user and post.context.comments_enabled -%}
<div class="card-nest">
<div class="card small">
<b>{{ text "communities:label.create_reply" }}</b>
@ -47,7 +47,7 @@
<div class="flex gap-2">
{{ components::emoji_picker(element_id="content",
render_dialog=true) }} {% if is_supporter %} {{
render_dialog=true) }} {% if is_supporter -%} {{
components::file_picker(files_list_id="files_list") }} {% endif
%}
@ -57,7 +57,7 @@
</div>
</form>
</div>
{% endif %}
{%- endif %}
<div class="pillmenu">
<a href="#/replies" data-tab-button="replies" class="active">
{{ icon "newspaper" }}
@ -75,14 +75,14 @@
</a>
</div>
{% if (user and user.id == post.owner) or can_manage_posts %}
{% if (user and user.id == post.owner) or can_manage_posts -%}
<div class="pillmenu">
{% if user and user.id == post.owner %}
{% if user and user.id == post.owner -%}
<a href="/post/{{ post.id }}#/edit">
{{ icon "pen" }}
<span>{{ text "communities:label.edit_content" }}</span>
</a>
{% endif %}
{%- endif %}
<a href="/post/{{ post.id }}/likes">
{{ icon "heart" }}
@ -94,7 +94,7 @@
<span>{{ text "communities:action.configure" }}</span>
</a>
</div>
{% endif %}
{%- endif %}
<div class="flex flex-col gap-2 hidden" data-tab="configure">
<div class="card-nest w-full">
@ -218,7 +218,7 @@
</script>
</div>
{% if user and user.id == post.owner %}
{% if user and user.id == post.owner -%}
<div class="card-nest w-full hidden" data-tab="edit">
<div class="card small flex items-center gap-2">
{{ icon "pen" }}
@ -279,7 +279,7 @@
});
}
</script>
{% endif %}
{%- endif %}
<div class="card-nest w-full" data-tab="replies">
<div class="card small flex items-center gap-2">

View file

@ -2,20 +2,20 @@
<title>Post quotes - {{ config.name }}</title>
{% endblock %} {% block body %} {{ macros::nav() }}
<main class="flex flex-col gap-2">
{% if post.replying_to %}
{% if post.replying_to -%}
<a href="/post/{{ post.replying_to }}" class="button">
{{ icon "arrow-up" }}
<span>{{ text "communities:action.continue_thread" }}</span>
</a>
{% endif %}
{%- endif %}
<!-- prettier-ignore -->
<div style="display: contents;">
{% if post.context.repost and post.context.repost.reposting %}
{% if post.context.repost and post.context.repost.reposting -%}
{{ components::repost(repost=reposting, post=post, owner=owner, community=community, show_community=true, can_manage_post=can_manage_posts) }}
{% else %}
{{ components::post(post=post, owner=owner, question=question, community=community, show_community=true, can_manage_post=can_manage_posts) }}
{% endif %}
{%- endif %}
</div>
<div class="pillmenu">
@ -35,14 +35,14 @@
</a>
</div>
{% if (user and user.id == post.owner) or can_manage_posts %}
{% if (user and user.id == post.owner) or can_manage_posts -%}
<div class="pillmenu">
{% if user and user.id == post.owner %}
{% if user and user.id == post.owner -%}
<a href="/post/{{ post.id }}#/edit">
{{ icon "pen" }}
<span>{{ text "communities:label.edit_content" }}</span>
</a>
{% endif %}
{%- endif %}
<a href="/post/{{ post.id }}/likes">
{{ icon "heart" }}
<span>{{ text "communities:label.likes" }}</span>
@ -53,7 +53,7 @@
<span>{{ text "communities:action.configure" }}</span>
</a>
</div>
{% endif %}
{%- endif %}
<div class="card-nest w-full">
<div class="card small flex items-center gap-2">

View file

@ -2,20 +2,20 @@
<title>Post reposts - {{ config.name }}</title>
{% endblock %} {% block body %} {{ macros::nav() }}
<main class="flex flex-col gap-2">
{% if post.replying_to %}
{% if post.replying_to -%}
<a href="/post/{{ post.replying_to }}" class="button">
{{ icon "arrow-up" }}
<span>{{ text "communities:action.continue_thread" }}</span>
</a>
{% endif %}
{%- endif %}
<!-- prettier-ignore -->
<div style="display: contents;">
{% if post.context.repost and post.context.repost.reposting %}
{% if post.context.repost and post.context.repost.reposting -%}
{{ components::repost(repost=reposting, post=post, owner=owner, community=community, show_community=true, can_manage_post=can_manage_posts) }}
{% else %}
{{ components::post(post=post, owner=owner, question=question, community=community, show_community=true, can_manage_post=can_manage_posts) }}
{% endif %}
{%- endif %}
</div>
<div class="pillmenu">
@ -35,14 +35,14 @@
</a>
</div>
{% if (user and user.id == post.owner) or can_manage_posts %}
{% if (user and user.id == post.owner) or can_manage_posts -%}
<div class="pillmenu">
{% if user and user.id == post.owner %}
{% if user and user.id == post.owner -%}
<a href="/post/{{ post.id }}#/edit">
{{ icon "pen" }}
<span>{{ text "communities:label.edit_content" }}</span>
</a>
{% endif %}
{%- endif %}
<a href="/post/{{ post.id }}/likes">
{{ icon "heart" }}
<span>{{ text "communities:label.likes" }}</span>
@ -53,7 +53,7 @@
<span>{{ text "communities:action.configure" }}</span>
</a>
</div>
{% endif %}
{%- endif %}
<div class="card-nest w-full">
<div class="card small flex items-center gap-2">

View file

@ -50,23 +50,23 @@
<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 %}
{% if profile.is_verified -%}
<span title="Verified" style="color: var(--color-primary);" class="flex items-center">
{{ icon "badge-check" }}
</span>
{% endif %}
{%- endif %}
{% if profile.permissions|has_supporter %}
{% if profile.permissions|has_supporter -%}
<span title="Supporter" style="color: var(--color-primary);" class="flex items-center">
{{ icon "star" }}
</span>
{% endif %}
{%- endif %}
{% if profile.permissions|has_staff_badge %}
{% if profile.permissions|has_staff_badge -%}
<span title="Staff" style="color: var(--color-primary);" class="flex items-center">
{{ icon "shield-user" }}
</span>
{% endif %}
{%- endif %}
</h3>
<span class="fade">{{ profile.username }}</span>
@ -77,9 +77,9 @@
class="card flex flex-col items-center gap-2"
id="social"
>
{% if profile.settings.status %}
{% if profile.settings.status -%}
<p>{{ profile.settings.status }}</p>
{% endif %}
{%- endif %}
<div class="w-full flex">
<a
@ -98,14 +98,14 @@
</a>
</div>
{% if is_following_you %}
{% if is_following_you -%}
<b
class="notification chip w-content flex items-center gap-2"
>
{{ icon "heart" }}
<span>Follows you</span>
</b>
{% endif %}
{%- endif %}
</div>
</div>
@ -117,11 +117,11 @@
<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 %}
{% 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 %}
{%- endif %}
</div>
<div class="w-full flex justify-between items-center">
@ -157,21 +157,21 @@
</span>
</div>
</div>
{% endif %}
{%- endif %}
</div>
</div>
{% if not is_self and user %}
{% 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_blocking -%}
<button
onclick="toggle_follow_user(event)"
class="{% if is_following %} hidden{% endif %}"
class="{% if is_following -%} hidden{%- endif %}"
atto_tag="user.follow"
>
{{ icon "user-plus" }}
@ -180,7 +180,7 @@
<button
onclick="toggle_follow_user(event)"
class="quaternary red{% if not is_following %} hidden{% endif %}"
class="quaternary red{% if not is_following -%} hidden{%- endif %}"
atto_tag="user.unfollow"
>
{{ icon "user-minus" }}
@ -202,7 +202,7 @@
{{ icon "shield-off" }}
<span>{{ text "auth:action.unblock" }}</span>
</button>
{% endif %} {% if not user.settings.private_chats or
{%- endif %} {% if not user.settings.private_chats or
is_following_you %}
<button
onclick="create_group_chat()"
@ -211,7 +211,7 @@
{{ icon "message-circle" }}
<span>{{ text "auth:action.message" }}</span>
</button>
{% endif %} {% if is_helper %}
{%- endif %} {% if is_helper -%}
<a
href="/mod_panel/profile/{{ profile.id }}"
class="button quaternary"
@ -219,7 +219,7 @@
{{ icon "shield" }}
<span>{{ text "general:action.manage" }}</span>
</a>
{% endif %}
{%- endif %}
<script>
globalThis.create_group_chat = async () => {
@ -320,7 +320,7 @@
</script>
</div>
</div>
{% endif %} {% if not profile.settings.private_communities or
{%- 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">
@ -337,7 +337,7 @@
{% endfor %}
</div>
</div>
{% endif %}
{%- endif %}
<div class="flex flex-col gap-2" id="connections">
{% for key, value in profile.connections %} {% if
@ -355,7 +355,7 @@
{{ icon "external-link" }}
</button>
</a>
{% endif %} {% endfor %}
{%- endif %} {% endfor %}
</div>
</div>
@ -365,7 +365,7 @@
</div>
</div>
</article>
{% if not is_self and profile.settings.warning %}
{% if not is_self and profile.settings.warning -%}
<script>
setTimeout(() => {
// check for warning
@ -376,5 +376,5 @@
]);
}, 150);
</script>
{% endif %} {% if not use_user_theme %} {{ components::theme(user=profile,
theme_preference=profile.settings.profile_theme) }} {% endif %} {% endblock %}
{%- endif %} {% if not use_user_theme -%} {{ components::theme(user=profile,
theme_preference=profile.settings.profile_theme) }} {%- endif %} {% endblock %}

View file

@ -18,7 +18,7 @@
<span>{{ text "auth:label.blocked_profile_message" }}</span>
<div class="card w-full secondary flex gap-2">
{% if user %} {% if not is_blocking %}
{% if user -%} {% if not is_blocking -%}
<button onclick="toggle_block_user()" class="quaternary red">
{{ icon "shield" }}
<span>{{ text "auth:action.block" }}</span>
@ -28,7 +28,7 @@
{{ icon "shield-off" }}
<span>{{ text "auth:action.unblock" }}</span>
</button>
{% endif %}
{%- endif %}
<script>
globalThis.toggle_block_user = async () => {
@ -52,7 +52,7 @@
});
};
</script>
{% endif %}
{%- endif %}
<a href="/" class="button red quaternary">
{{ icon "x" }}

View file

@ -5,7 +5,7 @@ profile.settings.allow_anonymous_questions) %}
{{ components::create_question_form(receiver=profile.id,
header=profile.settings.motivational_header) }}
</div>
{% endif %} {% if not tag and pinned|length != 0 %}
{%- endif %} {% if not tag and pinned|length != 0 -%}
<div class="card-nest">
<div class="card small flex gap-2 items-center">
{{ icon "pin" }}
@ -15,37 +15,51 @@ profile.settings.allow_anonymous_questions) %}
<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 %}
{% 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 %}
{%- endif %}
{%- endif %}
{% endfor %}
</div>
</div>
{% endif %}
{%- endif %}
<div class="card-nest">
<div class="card small flex gap-2 items-center">
{% if not tag %} {{ icon "clock" }}
<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 %}
<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 %}
{% 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 %}
{%- endif %}
{%- endif %}
{% endfor %}
{{ components::pagination(page=page, items=posts|length, key="&tag=", value=tag) }}

View file

@ -18,10 +18,10 @@
<span>{{ text "auth:label.private_profile_message" }}</span>
<div class="card w-full secondary flex gap-2">
{% if user %} {% if not is_following %}
{% if user -%} {% if not is_following -%}
<button
onclick="toggle_follow_user(event)"
class="{% if follow_requested %} hidden{% endif %}"
class="{% if follow_requested -%} hidden{%- endif %}"
atto_tag="user.follow_request"
>
{{ icon "user-plus" }}
@ -30,7 +30,7 @@
<button
onclick="cancel_follow_user(event)"
class="quaternary red{% if not follow_requested %} hidden{% endif %}"
class="quaternary red{% if not follow_requested -%} hidden{%- endif %}"
atto_tag="user.cancel_request"
>
{{ icon "user-minus" }}
@ -45,7 +45,7 @@
{{ icon "user-minus" }}
<span>{{ text "auth:action.unfollow" }}</span>
</button>
{% endif %} {% if not is_blocking %}
{%- endif %} {% if not is_blocking -%}
<button onclick="toggle_block_user()" class="quaternary red">
{{ icon "shield" }}
<span>{{ text "auth:action.block" }}</span>
@ -55,7 +55,7 @@
{{ icon "shield-off" }}
<span>{{ text "auth:action.unblock" }}</span>
</button>
{% endif %}
{%- endif %}
<script>
globalThis.toggle_follow_user = async (e) => {
@ -149,7 +149,7 @@
});
};
</script>
{% endif %}
{%- endif %}
<a href="/" class="button red quaternary">
{{ icon "x" }}

View file

@ -2,12 +2,12 @@
<title>Settings - {{ config.name }}</title>
{% endblock %} {% block body %} {{ macros::nav() }}
<main class="flex flex-col gap-2">
{% if profile.id != user.id %}
{% if profile.id != user.id -%}
<div class="card w-full red flex gap-2 items-center">
{{ icon "skull" }}
<b>Editing other user's settings! Please be careful.</b>
</div>
{% endif %}
{%- endif %}
<div class="pillmenu">
<a data-tab-button="account" class="active" href="#/account">
@ -65,12 +65,12 @@
<span>{{ text "settings:tab.uploads" }}</span>
</a>
{% if config.stripe %}
{% if config.stripe -%}
<a data-tab-button="account/billing" href="#/account/billing">
{{ icon "credit-card" }}
<span>{{ text "settings:tab.billing" }}</span>
</a>
{% endif %}
{%- endif %}
</div>
<div class="card-nest" ui_ident="home_timeline">
@ -84,59 +84,59 @@
>
<option
value="MyCommunities"
selected="{% if home == '/' %}true{% else %}false{% endif %}"
selected="{% if home == '/' -%}true{% else %}false{%- endif %}"
>
My communities
</option>
<option
value="MyCommunitiesQuestions"
selected="{% if home == '/questions' %}true{% else %}false{% endif %}"
selected="{% if home == '/questions' -%}true{% else %}false{%- endif %}"
>
My communities (questions)
</option>
<option
value="PopularPosts"
selected="{% if home == '/popular' %}true{% else %}false{% endif %}"
selected="{% if home == '/popular' -%}true{% else %}false{%- endif %}"
>
Popular
</option>
<option
value="PopularQuestions"
selected="{% if home == '/popular/questions' %}true{% else %}false{% endif %}"
selected="{% if home == '/popular/questions' -%}true{% else %}false{%- endif %}"
>
Popular (questions)
</option>
<option
value="FollowingPosts"
selected="{% if home == '/following' %}true{% else %}false{% endif %}"
selected="{% if home == '/following' -%}true{% else %}false{%- endif %}"
>
Following
</option>
<option
value="FollowingQuestions"
selected="{% if home == '/following/questions' %}true{% else %}false{% endif %}"
selected="{% if home == '/following/questions' -%}true{% else %}false{%- endif %}"
>
Following (questions)
</option>
<option
value="AllPosts"
selected="{% if home == '/all' %}true{% else %}false{% endif %}"
selected="{% if home == '/all' -%}true{% else %}false{%- endif %}"
>
All
</option>
<option
value="AllQuestions"
selected="{% if home == '/all/questions' %}true{% else %}false{% endif %}"
selected="{% if home == '/all/questions' -%}true{% else %}false{%- endif %}"
>
All (questions)
</option>
{% for stack in stacks %}
<option
value='{"Stack":"{{ stack.id }}"}'
selected="{% if home is ending_with(stack.id|as_str) %}true{% else %}false{% endif %}"
selected="{% if home is ending_with(stack.id|as_str) -%}true{% else %}false{%- endif %}"
>
{{ stack.name }} (stack)
</option>
@ -265,7 +265,7 @@
</div>
<div class="card flex flex-col gap-2">
{% if profile.totp|length == 0 %}
{% if profile.totp|length == 0 -%}
<div id="totp_stuff" style="display: none">
<span
>Scan this QR code in a TOTP authenticator
@ -312,7 +312,7 @@
Disable TOTP 2FA
</button>
</div>
{% endif %}
{%- endif %}
</div>
</div>
@ -569,7 +569,7 @@
</div>
<div class="card flex flex-col gap-2 secondary">
{% if config.stripe %}
{% if config.stripe -%}
<div class="card-nest" ui_ident="supporter_card">
<div class="card small flex items-center gap-2">
{{ icon "star" }}
@ -577,7 +577,7 @@
</div>
<div class="card flex flex-col gap-2">
{% if is_supporter %}
{% if is_supporter -%}
<p>
You <b>are</b> a supporter! Thank you for all
that you do. You can manage your billing
@ -617,6 +617,7 @@
<li>Create infinite stack timelines</li>
<li>Ability to upload images to posts</li>
<li>Save infinite post drafts</li>
<li>Ability to search through all posts</li>
</ul>
<a
@ -631,10 +632,10 @@
completing payment. It is required to manage
your billing settings.</span
>
{% endif %}
{%- endif %}
</div>
</div>
{% endif %}
{%- endif %}
</div>
</div>
</div>
@ -727,7 +728,7 @@
"
>{{ token[1] }}</b
>
{% if is_helper %}
{% if is_helper -%}
<span class="flex gap-2 items-center">
<span class="fade"
><a href="/api/v1/auth/user/find_by_ip/{{ token[0] }}"
@ -737,7 +738,7 @@
</span>
{% else %}
<span class="fade"><code>{{ token[0] }}</code></span>
{% endif %}
{%- endif %}
<span class="fade date">{{ token[2] }}</span>
</div>
@ -754,7 +755,7 @@
<div class="w-full hidden flex flex-col gap-2" data-tab="theme">
<div class="card tertiary flex flex-col gap-2" id="theme_settings">
{% if failing_color_keys|length > 0 %}
{% if failing_color_keys|length > 0 -%}
<div
class="card flex flex-col gap-2"
style="background: white; color: black"
@ -773,7 +774,7 @@
{% endfor %}
</ul>
</div>
{% endif %}
{%- endif %}
<div
class="card w-full flex flex-wrap gap-2"
@ -804,19 +805,19 @@
>
<option
value="Auto"
selected="{% if user.settings.theme_preference == 'Auto' %}true{% else %}false{% endif %}"
selected="{% if user.settings.theme_preference == 'Auto' -%}true{% else %}false{%- endif %}"
>
Auto
</option>
<option
value="Light"
selected="{% if user.settings.theme_preference == 'Light' %}true{% else %}false{% endif %}"
selected="{% if user.settings.theme_preference == 'Light' -%}true{% else %}false{%- endif %}"
>
Light
</option>
<option
value="Dark"
selected="{% if user.settings.theme_preference == 'Dark' %}true{% else %}false{% endif %}"
selected="{% if user.settings.theme_preference == 'Dark' -%}true{% else %}false{%- endif %}"
>
Dark
</option>
@ -839,19 +840,19 @@
>
<option
value="Auto"
selected="{% if user.settings.profile_theme == 'Auto' %}true{% else %}false{% endif %}"
selected="{% if user.settings.profile_theme == 'Auto' -%}true{% else %}false{%- endif %}"
>
Auto
</option>
<option
value="Light"
selected="{% if user.settings.profile_theme == 'Light' %}true{% else %}false{% endif %}"
selected="{% if user.settings.profile_theme == 'Light' -%}true{% else %}false{%- endif %}"
>
Light
</option>
<option
value="Dark"
selected="{% if user.settings.profile_theme == 'Dark' %}true{% else %}false{% endif %}"
selected="{% if user.settings.profile_theme == 'Dark' -%}true{% else %}false{%- endif %}"
>
Dark
</option>
@ -885,7 +886,7 @@
{{ icon "spotify" }}
<span>Spotify</span>
</button>
{% endif %} {% if config.connections.last_fm_key and not
{%- endif %} {% if config.connections.last_fm_key and not
profile.connections.LastFm %}
<button
class="quaternary"
@ -894,7 +895,7 @@
{{ icon "last_fm" }}
<span>Last.fm</span>
</button>
{% endif %}
{%- endif %}
</div>
{% for key, value in profile.connections %}
@ -904,13 +905,13 @@
<!-- prettier-ignore -->
<b class="flex items-center gap-2">
{% if value[0].data.name %}
{% if value[0].data.name -%}
<span>{{ value[0].data.name }}</span>
<span style="display: contents;" title="Verified connection">{{ icon "badge-check" }}</span>
{% else %}
<span>{{ key }}</span>
<span style="display: contents;">{{ icon "badge-alert" }}</span>
{% endif %}
{%- endif %}
</b>
</div>
@ -926,7 +927,7 @@
<!-- prettier-ignore -->
<input
type="checkbox"
{% if value[0].show_on_profile %}checked{% endif %}
{% if value[0].show_on_profile -%}checked{%- endif %}
id="{{ key }}-shown"
onchange="trigger('connections::push_con_shown', ['{{ key }}', event.target.checked])"
class="w-content"

View file

@ -16,14 +16,14 @@ macros -%}
<link rel="stylesheet" href="/css/style.css" />
{% if user %}
{% if user -%}
<script>
window.localStorage.setItem(
"tetratto:theme",
"{{ user.settings.theme_preference }}",
);
</script>
{% endif %}
{%- endif %}
<script src="/js/loader.js"></script>
<script defer async src="/js/atto.js"></script>
@ -69,7 +69,7 @@ macros -%}
<div id="page">
<!-- prettier-ignore -->
{% if user and user.id == 0 %}
{% if user and user.id == 0 -%}
<article>
<main>
<div class="card-nest">
@ -89,7 +89,7 @@ macros -%}
</div>
</main>
</article>
{% else %} {% block body %}{% endblock %} {% endif %}
{% else %} {% block body %}{% endblock %} {%- endif %}
<!-- html_footer_goes_here -->
</div>
@ -126,7 +126,7 @@ macros -%}
});
</script>
{% if user %}
{% if user -%}
<script data-turbo-permanent="true" id="update-seen-script">
document.documentElement.addEventListener("turbo:load", () => {
trigger("me::seen");
@ -141,7 +141,7 @@ macros -%}
}
});
</script>
{% endif %}
{%- endif %}
<!-- dialogs -->
<dialog id="link_filter">
@ -294,7 +294,7 @@ macros -%}
</a>
</div>
{% if user %}
{% if user -%}
<dialog id="tokens_dialog">
<div class="inner flex flex-col gap-2">
<form
@ -345,7 +345,7 @@ macros -%}
</div>
</div>
</dialog>
{% endif %} {% if user and use_user_theme %} {{
{%- endif %} {% if user and use_user_theme -%} {{
components::theme(user=user,
theme_preference=user.settings.theme_preference) }}
<script>
@ -353,7 +353,7 @@ macros -%}
trigger("atto::use_theme_preference");
}, 150);
</script>
{% endif %} {% if user and user.connections.Spotify and
{%- endif %} {% if user and user.connections.Spotify and
config.connections.spotify_client_id and
user.connections.Spotify[0].data.token and
user.connections.Spotify[0].data.refresh_token %}
@ -439,6 +439,6 @@ macros -%}
}
}, 150);
</script>
{% endif %}
{%- endif %}
</body>
</html>

View file

@ -2,7 +2,7 @@
<title>My stacks - {{ config.name }}</title>
{% endblock %} {% block body %} {{ macros::nav() }}
<main class="flex flex-col gap-2">
{{ macros::timelines_nav(selected="stacks") }} {% if user %}
{{ macros::timelines_nav(selected="stacks") }} {% if user -%}
<div class="card-nest">
<div class="card small">
<b>{{ text "stacks:label.create_new" }}</b>
@ -30,7 +30,7 @@
</button>
</form>
</div>
{% endif %}
{%- endif %}
<div class="card-nest w-full">
<div class="card small flex items-center justify-between gap-2">

View file

@ -25,13 +25,13 @@
<select onchange="save_privacy(event)">
<option
value="Private"
selected="{% if stack.privacy == 'Private' %}true{% else %}false{% endif %}"
selected="{% if stack.privacy == 'Private' -%}true{% else %}false{%- endif %}"
>
Private
</option>
<option
value="Public"
selected="{% if stack.privacy == 'Public' %}true{% else %}false{% endif %}"
selected="{% if stack.privacy == 'Public' -%}true{% else %}false{%- endif %}"
>
Public
</option>
@ -48,13 +48,13 @@
<select onchange="save_mode(event)">
<option
value="Include"
selected="{% if stack.mode == 'Include' %}true{% else %}false{% endif %}"
selected="{% if stack.mode == 'Include' -%}true{% else %}false{%- endif %}"
>
Include
</option>
<option
value="Exclude"
selected="{% if stack.mode == 'Exclude' %}true{% else %}false{% endif %}"
selected="{% if stack.mode == 'Exclude' -%}true{% else %}false{%- endif %}"
>
Exclude
</option>
@ -71,13 +71,13 @@
<select onchange="save_sort(event)">
<option
value="Created"
selected="{% if stack.sort == 'Created' %}true{% else %}false{% endif %}"
selected="{% if stack.sort == 'Created' -%}true{% else %}false{%- endif %}"
>
Created
</option>
<option
value="Likes"
selected="{% if stack.sort == 'Likes' %}true{% else %}false{% endif %}"
selected="{% if stack.sort == 'Likes' -%}true{% else %}false{%- endif %}"
>
Likes
</option>

View file

@ -10,7 +10,7 @@
<span>{{ stack.name }}</span>
</div>
{% if user and user.id == stack.owner %}
{% if user and user.id == stack.owner -%}
<a
href="/stacks/{{ stack.id }}/manage"
class="button quaternary small"
@ -18,23 +18,23 @@
{{ icon "pencil" }}
<span>{{ text "general:action.manage" }}</span>
</a>
{% endif %}
{%- endif %}
</div>
<!-- prettier-ignore -->
<div class="card w-full flex flex-col gap-2">
{% if list|length == 0 %}
{% if list|length == 0 -%}
<p>No posts yet! Maybe <a href="/stacks/{{ stack.id }}/manage#/users">add a user to this stack</a>!</p>
{% endif %}
{%- endif %}
{% for post in list %}
{% if post[2].read_access == "Everybody" %}
{% if post[0].context.repost and post[0].context.repost.reposting %}
{% 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) }}
{% else %}
{{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2]) }}
{% endif %}
{% endif %}
{%- endif %}
{%- endif %}
{% endfor %}
{{ components::pagination(page=page, items=list|length) }}

View file

@ -4,7 +4,7 @@
<main class="flex flex-col gap-2">
{{ macros::timelines_nav(selected="all") }} {{
macros::timelines_secondary_nav(posts="/all", questions="/all/questions") }}
{% if not user %}
{% if not user -%}
<div class="card-nest">
<div class="card small flex items-center gap-2">
{{ icon "heart" }}
@ -23,18 +23,18 @@
</a>
</div>
</div>
{% endif %}
{%- endif %}
<!-- prettier-ignore -->
<div class="card w-full flex flex-col gap-2">
{% for post in list %}
{% if post[2].read_access == "Everybody" %}
{% if post[0].context.repost and post[0].context.repost.reposting %}
{% 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) }}
{% else %}
{{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2]) }}
{% endif %}
{% endif %}
{%- endif %}
{%- endif %}
{% endfor %}
{{ components::pagination(page=page, items=list|length) }}

View file

@ -9,13 +9,13 @@
<!-- prettier-ignore -->
<div class="card w-full flex flex-col gap-2">
{% for post in list %}
{% if post[2].read_access == "Everybody" %}
{% if post[0].context.repost and post[0].context.repost.reposting %}
{% 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) }}
{% else %}
{{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2]) }}
{% endif %}
{% endif %}
{%- endif %}
{%- endif %}
{% endfor %}
{{ components::pagination(page=page, items=list|length) }}

View file

@ -6,7 +6,7 @@
{{ macros::timelines_nav(selected="home") }} {{
macros::timelines_secondary_nav(posts="/", questions="/questions") }}
{% if list|length == 0 and page == 0 %}
{% if list|length == 0 and page == 0 -%}
<div class="card-nest">
<div class="card">
<b>✨ Welcome to <i>{{ config.name }}</i>!</b>
@ -26,15 +26,15 @@
<!-- prettier-ignore -->
<div class="card w-full flex flex-col gap-2">
{% for post in list %}
{% if post[0].context.repost and post[0].context.repost.reposting %}
{% 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) }}
{% else %}
{{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2]) }}
{% endif %}
{%- endif %}
{% endfor %}
{{ components::pagination(page=page, items=list|length) }}
</div>
{% endif %}
{%- endif %}
</main>
{% endblock %}

View file

@ -9,13 +9,13 @@
<!-- prettier-ignore -->
<div class="card w-full flex flex-col gap-2">
{% for post in list %}
{% if post[2].read_access == "Everybody" %}
{% if post[0].context.repost and post[0].context.repost.reposting %}
{% 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) }}
{% else %}
{{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2]) }}
{% endif %}
{% endif %}
{%- endif %}
{%- endif %}
{% endfor %}
{{ components::pagination(page=page, items=list|length) }}

View file

@ -0,0 +1,75 @@
{% extends "root.html" %} {% block head %}
<title>Search - {{ config.name }}</title>
{% endblock %} {% block body %} {{ macros::nav() }}
<main class="flex flex-col gap-2">
{{ macros::timelines_nav(selected="search") }}
<div class="card-nest w-full">
<div class="card small flex items-center justify-between gap-2">
<div class="flex items-center gap-2">
{{ icon "search" }} {% if not profile -%}
<span>{{ text "general:link.search" }}</span>
{% else %}
<span>{{ components::full_username(user=profile) }}</span>
{%- endif %}
</div>
</div>
<div class="card w-full flex flex-col gap-2">
{% if not profile and not user.permissions|has_supporter -%} {{
components::supporter_ad(body="Become a supporter for full-site
search!") }} {% else %}
<form class="flex flex-col gap-2">
<div class="flex flex-row gap-2">
<input
type="text"
name="query"
id="query"
required
value="{{ query }}"
placeholder="{% if profile -%}Search {{ profile.username }}'s posts{% else %}Search all posts{%- endif %}"
autocomplete="off"
/>
{% if profile -%}
<input
type="text"
class="hidden"
value="{{ profile.id }}"
name="profile"
id="profile"
/>
{%- endif %}
<button class="small square">{{ icon "search" }}</button>
</div>
{% if config.manuals.search_help -%}
<span
><a href="{{ config.manuals.search_help }}">
Search help
</a></span
>
{%- endif %}
</form>
{%- endif %}
<!-- prettier-ignore -->
{% for post in list %}
{% 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) }}
{% else %}
{{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2]) }}
{%- endif %}
{%- endif %}
{% endfor %}
{% if profile -%}
{{ components::pagination(page=page, items=list|length, key="&profile=" ~ profile.id, value="&query=" ~ query) }}
{% else %}
{{ components::pagination(page=page, items=list|length, key="&query=" ~ query) }}
{%- endif %}
</div>
</div>
</main>
{% endblock %}

View file

@ -1,12 +1,15 @@
use super::{PaginatedQuery, render_error};
use crate::{State, assets::initial_context, get_lang, get_user_from_token};
use crate::{
assets::initial_context, check_user_blocked_or_private, get_lang, get_user_from_token, State,
};
use axum::{
extract::{Path, Query},
response::{Html, IntoResponse},
Extension,
};
use axum_extra::extract::CookieJar;
use tetratto_core::model::{requests::ActionType, Error};
use serde::Deserialize;
use tetratto_core::model::{permissions::FinePermission, requests::ActionType, Error};
use std::fs::read_to_string;
use pathbufd::PathBufD;
@ -497,3 +500,96 @@ pub async fn markdown_document_request(
// return
Ok(Html(data.1.render("misc/markdown.html", &context).unwrap()))
}
#[derive(Deserialize)]
pub struct SearchQuery {
#[serde(default)]
pub query: String,
#[serde(default)]
pub profile: usize,
#[serde(default)]
pub page: usize,
}
/// `/search`
pub async fn search_request(
jar: CookieJar,
Extension(data): Extension<State>,
Query(mut req): Query<SearchQuery>,
) -> impl IntoResponse {
let data = data.read().await;
let user = match get_user_from_token!(jar, data.0) {
Some(ua) => ua,
None => {
return Err(Html(
render_error(Error::NotAllowed, &jar, &data, &None).await,
));
}
};
if req.profile == 0 && !user.permissions.check(FinePermission::SUPPORTER) {
req.query = String::new();
}
req.query = req.query.trim().replace(" ", " & "); // change spaces into & for tsquery
let ignore_users = data.0.get_userblocks_receivers(user.id).await;
let list = if req.query.is_empty() {
Vec::new()
} else {
if req.profile != 0 {
match data
.0
.get_posts_by_user_searched(req.profile, 12, req.page, &req.query, &Some(&user))
.await
{
Ok(l) => match data
.0
.fill_posts_with_community(l, user.id, &ignore_users, &Some(user.clone()))
.await
{
Ok(l) => l,
Err(_) => Vec::new(),
},
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
}
} else {
match data.0.get_posts_searched(12, req.page, &req.query).await {
Ok(l) => match data
.0
.fill_posts_with_community(l, user.id, &ignore_users, &Some(user.clone()))
.await
{
Ok(l) => l,
Err(_) => Vec::new(),
},
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
}
}
};
let profile = if req.profile != 0 {
Some(match data.0.get_user_by_id(req.profile).await {
Ok(ua) => {
check_user_blocked_or_private!(Some(user.clone()), ua, data, jar);
ua
}
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
})
} else {
None
};
let lang = get_lang!(jar, data.0);
let mut context = initial_context(&data.0.0, lang, &Some(user)).await;
context.insert("list", &list);
context.insert("profile", &profile);
context.insert("query", &req.query);
context.insert("page", &req.page);
Ok(Html(
data.1.render("timelines/search.html", &context).unwrap(),
))
}

View file

@ -28,6 +28,7 @@ pub fn routes() -> Router {
.route("/popular", get(misc::popular_request))
.route("/following", get(misc::following_request))
.route("/all", get(misc::all_request))
.route("/search", get(misc::search_request))
// question timelines
.route("/questions", get(misc::index_questions_request))
.route("/popular/questions", get(misc::popular_questions_request))

View file

@ -1,6 +1,6 @@
[package]
name = "tetratto-core"
version = "3.1.0"
version = "4.0.0"
edition = "2024"
[features]

View file

@ -196,6 +196,21 @@ pub struct StripeConfig {
pub billing_portal_url: String,
}
/// Manuals config (search help, etc)
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct ManualsConfig {
/// The page shown for help with search syntax.
pub search_help: String,
}
impl Default for ManualsConfig {
fn default() -> Self {
Self {
search_help: "".to_string(),
}
}
}
/// Configuration file
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Config {
@ -259,6 +274,9 @@ pub struct Config {
pub html_footer_path: String,
#[serde(default)]
pub stripe: Option<StripeConfig>,
/// The relative paths to manuals.
#[serde(default)]
pub manuals: ManualsConfig,
}
fn default_name() -> String {
@ -307,12 +325,15 @@ fn default_banned_usernames() -> Vec<String> {
"moderator".to_string(),
"api".to_string(),
"communities".to_string(),
"community".to_string(),
"notifs".to_string(),
"notification".to_string(),
"post".to_string(),
"void".to_string(),
"anonymous".to_string(),
"stacks".to_string(),
"stack".to_string(),
"search".to_string(),
]
}
@ -328,6 +349,10 @@ fn default_connections() -> ConnectionsConfig {
ConnectionsConfig::default()
}
fn default_manuals() -> ManualsConfig {
ManualsConfig::default()
}
impl Default for Config {
fn default() -> Self {
Self {
@ -348,6 +373,7 @@ impl Default for Config {
connections: default_connections(),
html_footer_path: String::new(),
stripe: None,
manuals: default_manuals(),
}
}
}

View file

@ -13,5 +13,6 @@ CREATE TABLE IF NOT EXISTS posts (
comment_count INT NOT NULL,
-- ...
uploads TEXT NOT NULL,
is_deleted INT NOT NULL
is_deleted INT NOT NULL,
tsvector_content tsvector GENERATED ALWAYS AS (to_tsvector ('english', coalesce(content, ''))) STORED
)

View file

@ -52,6 +52,37 @@ macro_rules! private_post_replying {
continue;
}
};
($post:ident, $replying_posts:ident, id=$user_id:ident, $data:ident) => {
// post owner is not following us
// check if we're the owner of the post the post is replying to
// all routes but 1 must lead to continue
if let Some(replying) = $post.replying_to {
if replying != 0 {
if let Some(post) = $replying_posts.get(&replying) {
// we've seen this post before
if post.owner != $user_id {
// we aren't the owner of this post,
// so we can't see their comment
continue;
}
} else {
// we haven't seen this post before
let post = $data.get_post_by_id(replying).await?;
if post.owner != $user_id {
continue;
}
$replying_posts.insert(post.id, post);
}
} else {
continue;
}
} else {
continue;
}
};
}
impl DataManager {
@ -317,6 +348,7 @@ impl DataManager {
let mut seen_before: HashMap<(usize, usize), (User, Community)> = HashMap::new();
let mut seen_user_follow_statuses: HashMap<(usize, usize), bool> = HashMap::new();
let mut replying_posts: HashMap<usize, Post> = HashMap::new();
for post in posts {
if post.is_deleted {
@ -355,9 +387,8 @@ impl DataManager {
if user_id != ua.id {
if let Some(is_following) = seen_user_follow_statuses.get(&(ua.id, user_id))
{
if !is_following && (ua.id != user_id) {
// post owner is not following us
continue;
if !is_following {
private_post_replying!(post, replying_posts, id = user_id, self);
}
} else {
if self
@ -367,7 +398,7 @@ impl DataManager {
{
// post owner is not following us
seen_user_follow_statuses.insert((ua.id, user_id), false);
continue;
private_post_replying!(post, replying_posts, id = user_id, self);
}
seen_user_follow_statuses.insert((ua.id, user_id), true);
@ -428,9 +459,9 @@ impl DataManager {
let res = query_rows!(
&conn,
&format!(
"SELECT * FROM posts WHERE owner = $1 AND replying_to = 0 AND NOT context LIKE '%\"is_profile_pinned\":true%' {} ORDER BY created DESC LIMIT $2 OFFSET $3",
"SELECT * FROM posts WHERE owner = $1 AND replying_to = 0 AND NOT (context::json->>'is_profile_pinned')::boolean {} ORDER BY created DESC LIMIT $2 OFFSET $3",
if hide_nsfw {
"AND NOT context LIKE '%\"is_nsfw\":true%'"
"AND NOT (context::json->>'is_nsfw')::boolean"
} else {
""
}
@ -446,6 +477,101 @@ impl DataManager {
Ok(res.unwrap())
}
/// Get all posts from the given user (searched).
///
/// # Arguments
/// * `id` - the ID of the user the requested posts belong to
/// * `batch` - the limit of posts in each page
/// * `page` - the page number
/// * `text_query` - the search query
/// * `user` - the user who is viewing the posts
pub async fn get_posts_by_user_searched(
&self,
id: usize,
batch: usize,
page: usize,
text_query: &str,
user: &Option<&User>,
) -> Result<Vec<Post>> {
let other_user = self.get_user_by_id(id).await?;
let conn = match self.connect().await {
Ok(c) => c,
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
};
// check if we should hide nsfw posts
let mut hide_nsfw: bool = true;
if let Some(ua) = user {
if ua.id == other_user.id {
hide_nsfw = false
}
}
if other_user.settings.private_profile {
hide_nsfw = false;
}
// ...
let res = query_rows!(
&conn,
&format!(
"SELECT * FROM posts WHERE owner = $1 AND tsvector_content @@ to_tsquery($2) AND replying_to = 0 AND NOT (context::json->>'is_profile_pinned')::boolean {} ORDER BY created DESC LIMIT $3 OFFSET $4",
if hide_nsfw {
"AND NOT (context::json->>'is_nsfw')::boolean"
} else {
""
}
),
params![
&(id as i64),
&text_query,
&(batch as i64),
&((page * batch) as i64)
],
|x| { Self::get_post_from_row(x) }
);
if res.is_err() {
return Err(Error::GeneralNotFound("post".to_string()));
}
Ok(res.unwrap())
}
/// Get all post (searched).
///
/// # Arguments
/// * `batch` - the limit of posts in each page
/// * `page` - the page number
/// * `text_query` - the search query
pub async fn get_posts_searched(
&self,
batch: usize,
page: usize,
text_query: &str,
) -> Result<Vec<Post>> {
let conn = match self.connect().await {
Ok(c) => c,
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
};
// ...
let res = query_rows!(
&conn,
"SELECT * FROM posts WHERE tsvector_content @@ to_tsquery($1) AND replying_to = 0 AND NOT (context::json->>'is_profile_pinned')::boolean ORDER BY created DESC LIMIT $2 OFFSET $3",
params![&text_query, &(batch as i64), &((page * batch) as i64)],
|x| { Self::get_post_from_row(x) }
);
if res.is_err() {
return Err(Error::GeneralNotFound("post".to_string()));
}
Ok(res.unwrap())
}
/// Get all posts from the given user with the given tag (from most recent).
///
/// # Arguments
@ -487,7 +613,7 @@ impl DataManager {
&format!(
"SELECT * FROM posts WHERE owner = $1 AND context::json->>'tags' LIKE $2 {} ORDER BY created DESC LIMIT $3 OFFSET $4",
if hide_nsfw {
"AND NOT context LIKE '%\"is_nsfw\":true%'"
"AND NOT (context::json->>'is_nsfw')::boolean"
} else {
""
}

View file

@ -1,6 +1,6 @@
[package]
name = "tetratto-l10n"
version = "3.1.0"
version = "4.0.0"
edition = "2024"
authors.workspace = true
repository.workspace = true

View file

@ -1,6 +1,6 @@
[package]
name = "tetratto-shared"
version = "3.1.0"
version = "4.0.0"
edition = "2024"
authors.workspace = true
repository.workspace = true

View file

@ -1,5 +1,5 @@
{% if user %}
{% if user -%}
<script>
console.log("current user is {{ user.username }}");
</script>
{% endif %}
{%- endif %}

View file

@ -0,0 +1,4 @@
ALTER TABLE posts
ADD COLUMN tsvector_content tsvector GENERATED ALWAYS AS (to_tsvector ('english', coalesce(content, ''))) STORED;
CREATE INDEX tsvector_content_idx ON posts USING GIN (tsvector_content);