add: custom emojis

fix: don't show reposts of posts from blocked users
fix: don't show questions when they're from users you've blocked
This commit is contained in:
trisua 2025-05-10 21:58:02 -04:00
parent 9f187039e6
commit 275dd0a1eb
25 changed files with 697 additions and 61 deletions

View file

@ -327,7 +327,7 @@ hide_user_menu=true) }}
}
.message.grouped {
padding: 0.25rem 1rem 0.25rem calc(1rem + 0.5rem + 39px);
padding: 0.25rem 1rem 0.25rem calc(1rem + 0.5rem + 31px);
}
body:not(.sidebars_shown) .sidebar {

View file

@ -23,6 +23,11 @@
{{ icon "rss" }}
<span>{{ text "communities:tab.channels" }}</span>
</a>
{% endif %} {% if can_manage_emojis %}
<a href="#/emojis" data-tab-button="emojis">
{{ icon "smile" }}
<span>{{ text "communities:tab.emojis" }}</span>
</a>
{% endif %}
</div>
@ -436,6 +441,167 @@
});
}
</script>
{% endif %} {% if can_manage_emojis %}
<div
class="card tertiary w-full hidden flex flex-col gap-2"
data-tab="emojis"
>
{{ components::supporter_ad(body="Become a supporter to upload GIF
animated emojis!") }}
<div class="card-nest" ui_ident="change_banner">
<div class="card small flex items-center gap-2">
{{ icon "upload" }}
<b>{{ text "communities:label.upload" }}</b>
</div>
<form
class="card flex flex-col gap-2"
onsubmit="upload_emoji(event)"
>
<div class="flex flex-col gap-1">
<label for="name"
>{{ text "communities:label.name" }}</label
>
<input
type="text"
name="name"
id="name"
placeholder="name"
required
minlength="2"
maxlength="32"
/>
</div>
<div class="flex flex-col gap-1">
<label for="file"
>{{ text "communities:label.file" }}</label
>
<input
id="banner_file"
name="file"
type="file"
accept="image/png,image/jpeg,image/avif,image/webp"
class="w-full"
/>
</div>
<button>{{ text "communities:action.create" }}</button>
<span class="fade"
>Emojis can be a maximum of 256 KiB, or 512x512px (width x
height).</span
>
</form>
</div>
{% for emoji in emojis %}
<div
class="card secondary flex flex-wrap gap-2 items-center justify-between"
>
<div class="flex gap-2 items-center">
<img
src="/api/v1/communities/{{ community.id }}/emojis/{{ emoji.name }}"
alt="{{ emoji.name }}"
class="emoji"
loading="lazy"
/>
<b>{{ emoji.name }}</b>
</div>
<div class="flex gap-2">
<button
class="quaternary small"
onclick="rename_emoji('{{ emoji.id }}')"
>
{{ icon "pencil" }}
<span>{{ text "chats:action.rename" }}</span>
</button>
<button
class="quaternary small red"
onclick="remove_emoji('{{ emoji.id }}')"
>
{{ icon "x" }}
<span>{{ text "stacks:label.remove" }}</span>
</button>
</div>
</div>
{% endfor %}
</div>
<script>
globalThis.upload_emoji = (e) => {
e.preventDefault();
e.target.querySelector("button").style.display = "none";
fetch(
`/api/v1/communities/{{ community.id }}/emojis/${e.target.name.value}`,
{
method: "POST",
body: e.target.file.files[0],
},
)
.then((res) => res.json())
.then((res) => {
trigger("atto::toast", [
res.ok ? "success" : "error",
res.message,
]);
e.target.querySelector("button").removeAttribute("style");
});
alert("Emoji upload in progress. Please wait!");
};
globalThis.rename_emoji = async (id) => {
const name = await trigger("atto::prompt", ["New emoji name:"]);
if (!name) {
return;
}
fetch(`/api/v1/emojis_id/${id}/name`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name,
}),
})
.then((res) => res.json())
.then((res) => {
trigger("atto::toast", [
res.ok ? "success" : "error",
res.message,
]);
});
};
globalThis.remove_emoji = async (id) => {
if (
!(await trigger("atto::confirm", [
"Are you sure you would like to do this? This action is permanent.",
]))
) {
return;
}
fetch(`/api/v1/emojis_id/${id}`, {
method: "DELETE",
})
.then((res) => res.json())
.then((res) => {
trigger("atto::toast", [
res.ok ? "success" : "error",
res.message,
]);
});
};
</script>
{% endif %}
</main>

View file

@ -179,6 +179,9 @@ and show_community and community.id != config.town_square or question %}
<div
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">
<a href="/@{{ owner.username }}">
@ -1202,6 +1205,17 @@ show_kick=false, secondary=false) -%}
></emoji-picker>
<script>
setTimeout(async () => {
document.querySelector("emoji-picker").customEmoji =
await trigger("me::emojis");
const style = document.createElement("style");
style.textContent = `.custom-emoji { border-radius: 4px !important; } .category { font-weight: 600; }`;
document
.querySelector("emoji-picker")
.shadowRoot.appendChild(style);
}, 150);
document
.querySelector("emoji-picker")
.addEventListener("emoji-click", async (event) => {
@ -1214,14 +1228,20 @@ show_kick=false, secondary=false) -%}
return;
}
document.getElementById(
window.EMOJI_PICKER_TEXT_ID,
).value += ` :${await (
await fetch("/api/v1/lookup_emoji", {
method: "POST",
body: event.detail.unicode,
})
).text()}:`;
if (event.detail.unicode) {
document.getElementById(
window.EMOJI_PICKER_TEXT_ID,
).value += ` :${await (
await fetch("/api/v1/lookup_emoji", {
method: "POST",
body: event.detail.unicode,
})
).text()}:`;
} else {
document.getElementById(
window.EMOJI_PICKER_TEXT_ID,
).value += ` :${event.detail.emoji.shortcodes[0]}:`;
}
document.getElementById("emoji_dialog").close();
});

View file

@ -71,6 +71,10 @@
class="card flex flex-col items-center gap-2"
id="social"
>
{% if profile.settings.status %}
<p>{{ profile.settings.status }}</p>
{% endif %}
<div class="w-full flex">
<a
href="/@{{ profile.username }}/followers"

View file

@ -112,6 +112,7 @@ macros -%}
atto["hooks::check_reactions"]();
atto["hooks::tabs"]();
atto["hooks::spotify_time_text"](); // spotify durations
atto["hooks::verify_emoji"]();
if (document.getElementById("tokens")) {
trigger("me::render_token_picker", [

View file

@ -310,7 +310,7 @@
return;
}
fetch(`/api/v1/stacks/{{ stack.id }}`, {
fetch("/api/v1/stacks/{{ stack.id }}", {
method: "DELETE",
})
.then((res) => res.json())