autonomous.contact/public/js/reactions.js

269 lines
6.8 KiB
JavaScript
Raw Normal View History

2025-10-14 22:20:54 -07:00
const reactions_popup_width = 280;
const reactions_popup_height = 280;
const reactions_popup_styling = `
#reactionspopup {
position: fixed;
width: ${reactions_popup_width}px;
height: ${reactions_popup_height}px;
z-index: 100;
background: inherit;
overflow: hidden;
border: 1px solid var(--border-normal);
padding: 0.5rem;
text-align: center;
visibility: hidden;
display: none;
opacity: 0;
}
#reactionspopup[data-shown] {
visibility: visible;
display: block;
opacity: 1;
2025-10-14 22:20:54 -07:00
}
#reactionspopup .icon.close {
float: right;
margin: 0.5rem;
}
#reactionspopup input[name="search"] {
width: 80%;
}
#reactionspopup ul {
margin-top: 0.5rem;
padding: 0.5rem;
text-align: left;
overflow: scroll;
}
#reactionspopup ul li {
display: inline-block;
cursor: pointer;
}
#reactionspopup ul[data-filtered] li {
display: none;
visibility: hidden;
}
#reactionspopup ul[data-filtered] li[data-filtered] {
display: inline-block;
visibility: visible;
}
#reactionspopup #reactions-names-display {
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: 2rem;
background: inherit;
}
2025-10-15 17:50:48 -07:00
.reaction-container {
display: inline-block;
border: 1px solid var(--border-subtle);
border-radius: var(--border-radius);
margin-right: 0.5rem;
padding: 0.25rem;
font-size: large;
}
2025-10-14 22:20:54 -07:00
2025-10-15 17:50:48 -07:00
@media screen and (max-width: 480px) {
#reactionspopup {
left: 0 !important;
right: 0 !important;
bottom: 0 !important;
top: auto !important;
width: 100% !important;
2025-10-14 22:20:54 -07:00
}
2025-10-15 17:50:48 -07:00
}
2025-10-14 22:20:54 -07:00
2025-10-15 17:50:48 -07:00
`;
2025-10-14 22:20:54 -07:00
let reactions_popup;
let reactions_popup_form;
2025-10-15 17:50:48 -07:00
let reactions_popup_search_input;
2025-10-14 22:20:54 -07:00
let reactions_popup_parent_id_input;
let reactions_popup_emojis_list;
let reactions_popup_reaction_input;
function open_reactions_popup(event) {
2025-10-15 17:50:48 -07:00
const parent_event_id =
event.target?.closest("[data-current_event_id]")?.dataset?.current_event_id;
2025-10-14 22:20:54 -07:00
reactions_popup_parent_id_input.value = parent_event_id ?? "";
2025-10-15 17:50:48 -07:00
const position = get_best_coords_for_popup({
target_element: event.target.closest("[data-reactions]"),
popup: {
width: reactions_popup_width,
height: reactions_popup_height,
},
offset: {
x: 25,
y: 25,
},
2025-10-14 22:20:54 -07:00
});
reactions_popup.style.left = position.x + "px";
reactions_popup.style.top = position.y + "px";
reactions_popup.dataset.shown = true;
2025-10-15 17:50:48 -07:00
reactions_popup_search_input.focus();
2025-10-14 22:20:54 -07:00
}
function clear_reactions_popup() {
if (!reactions_popup) {
return;
}
delete reactions_popup.dataset.shown;
2025-10-14 22:20:54 -07:00
}
document.addEventListener("DOMContentLoaded", () => {
if (!document.getElementById("reactions-styling")) {
const style = document.createElement("style");
style.id = "reactions-styling";
style.innerHTML = reactions_popup_styling;
document.head.appendChild(style);
}
reactions_popup = document.createElement("div");
reactions_popup.id = "reactionspopup";
reactions_popup.innerHTML = `
<div class="icon close" onclick="clear_reactions_popup()"></div>
<form
id="reactions-selection-form"
data-smart="true"
method="POST"
2025-10-15 17:50:48 -07:00
on_reply="async (event) => { clear_reactions_popup(); await document.querySelectorAll( '[data-feed]' ).forEach((feed) => feed.__render(event)); }"
on_parsed="async (event) => { clear_reactions_popup(); await document.querySelectorAll( '[data-feed]' ).forEach((feed) => feed.__render(event)); }"
2025-10-14 22:20:54 -07:00
>
<input id="reactions-search-input" name="search" type="text" placeholder="Search..." data-skip="true" />
<input type="hidden" name="type" value="reaction" />
<input
type="hidden"
name="id"
generator="(_input, form) => 'TEMP-' + form.__submitted_at.toISOString()"
reset-on-submit
/>
<input
type="hidden"
name="meta.temp_id"
generator="(_input, form) => 'TEMP-' + form.__submitted_at.toISOString()"
reset-on-submit
/>
<input
type="hidden"
name="creator_id"
generator="() => { return APP.user?.id; }"
2025-10-14 22:20:54 -07:00
/>
<input
type="hidden"
name="timestamps.created"
generator="(_input, form) => form.__submitted_at.toISOString()"
reset-on-submit
/>
<input
type="hidden"
name="timestamps.updated"
generator="(_input, form) => form.__submitted_at.toISOString()"
reset-on-submit
/>
<input type="hidden" name="parent_id" reset-on-submit />
<input type="hidden" name="data.reaction" reset-on-submit />
<ul id="reactions-emojis-list">
${Object.keys(EMOJIS.MAP)
.map(
(emoji) =>
`<li data-emoji="${emoji}" data-names="${EMOJIS.MAP[emoji].map((name) => `:${name}:`).join(" ")}">${emoji}</li>`,
)
.join("\n")}
</ul>
</form>
<div id="reactions-names-display"></div>
`;
document.body.appendChild(reactions_popup);
reactions_popup_form = document.getElementById("reactions-selection-form");
APP.on("topic_changed", ({ topic_id }) => {
2025-10-14 22:20:54 -07:00
const reaction_topic_id = topic_id ?? document.body.dataset.topic;
reactions_popup_form.action = reaction_topic_id
? `/api/topics/${reaction_topic_id}/events`
: "";
});
2025-10-15 17:50:48 -07:00
reactions_popup_search_input = document.getElementById("reactions-search-input");
2025-10-14 22:20:54 -07:00
reactions_popup_parent_id_input = reactions_popup_form.querySelector('[name="parent_id"]');
reactions_popup_emojis_list = document.getElementById("reactions-emojis-list");
reactions_popup_reaction_input = reactions_popup_form.querySelector('[name="data.reaction"]');
reactions_popup_names_display = document.getElementById("reactions-names-display");
reactions_popup_emojis_list.querySelectorAll("li[data-emoji]").forEach((emoji_selector) => {
emoji_selector.addEventListener("click", (event) => {
event.preventDefault();
const selector = event.target;
const emoji = selector.dataset.emoji;
reactions_popup_reaction_input.value = emoji;
reactions_popup_form.requestSubmit();
});
});
reactions_popup_form.addEventListener("mouseover", (event) => {
reactions_popup_names_display.textContent = event.target.matches("li[data-names]")
? event.target.dataset.names
: "";
});
2025-10-15 17:50:48 -07:00
const debounced_search = debounce((event) => {
2025-10-14 22:20:54 -07:00
const prompt = event.target?.value;
const filtered = EMOJIS.autocomplete(prompt);
2025-10-15 17:50:48 -07:00
delete reactions_popup_emojis_list.dataset.filtered;
2025-10-14 22:20:54 -07:00
if (filtered.length) {
2025-10-15 17:50:48 -07:00
reactions_popup_emojis_list.dataset.filtered = true;
reactions_popup_emojis_list.querySelectorAll("li").forEach((li) => {
2025-10-14 22:20:54 -07:00
if (filtered.some((entry) => entry[0] === li.dataset.emoji)) {
li.dataset.filtered = true;
} else {
delete li.dataset.filtered;
}
});
}
}, 200);
2025-10-15 17:50:48 -07:00
reactions_popup_search_input.addEventListener("input", debounced_search);
reactions_popup_search_input.addEventListener("paste", debounced_search);
reactions_popup_search_input.addEventListener("change", debounced_search);
2025-10-14 22:20:54 -07:00
document.querySelector("body").addEventListener("click", (event) => {
2025-10-15 17:50:48 -07:00
const is_in_the_reactions_form = event?.target?.closest("#reactionspopup");
if (is_in_the_reactions_form) {
return;
}
2025-10-14 22:20:54 -07:00
const is_a_data_reactions_child = event?.target?.closest("[data-reactions]");
if (!is_a_data_reactions_child) {
clear_reactions_popup();
return;
}
event.preventDefault();
open_reactions_popup(event);
});
});