tawny/app/public/messages.js

463 lines
12 KiB
JavaScript

const STATE = {
id: 0,
chat_id: "",
observer: null,
is_loading: false,
stream_element: null,
last_message_time: 0,
replying_to: undefined,
last_read_receipt_load: 0,
};
function create_streamer(chat_id, hook_element) {
STATE.chat_id = chat_id;
STATE.stream_element = hook_element.parentElement;
clear_notifications();
STATE.observer = new IntersectionObserver(
(entries) => {
load_messages();
},
{
root: document.body,
rootMargin: "0px",
scrollMargin: "0px",
threshold: 1.0,
},
);
STATE.observer.observe(hook_element);
read_receipt();
}
function load_messages() {
if (STATE.is_loading) {
return;
}
STATE.is_loading = true;
STATE.id += 1;
fetch(
`/chats/_templates/chat/${STATE.chat_id}/messages/before/${STATE.last_message_time}?use_id=${STATE.id}`,
)
.then((res) => res.text())
.then((res) => {
setTimeout(() => {
STATE.is_loading = false;
}, 2000);
STATE.stream_element.innerHTML += res;
STATE.last_message_time = Number.parseInt(
document
.getElementById(`msgs_data_${STATE.id}`)
.getAttribute("data-first-message-time"),
);
if (document.getElementById(`msgs_quit_${STATE.id}`)) {
STATE.observer.disconnect();
console.log("quit");
} else {
STATE.observer.unobserve(
STATE.stream_element.querySelector(
"[ui_ident=data_marker]",
),
);
STATE.stream_element
.querySelector("[ui_ident=data_marker]")
.remove();
const element = document.createElement("div");
element.setAttribute("ui_ident", "data_marker");
STATE.stream_element.append(element);
STATE.observer.observe(element);
}
});
}
function render_message(id) {
STATE.is_loading = true;
fetch(`/chats/_templates/message/${id}`)
.then((res) => res.text())
.then((res) => {
STATE.is_loading = false;
STATE.stream_element.innerHTML = `${res}${STATE.stream_element.innerHTML}`;
mark_message_read();
scroll_bottom();
if (document.getElementById("delivered_read_status")) {
document.getElementById("delivered_read_status").remove(); // clear read receipt box
}
});
}
function scroll_bottom() {
STATE.stream_element.parentElement.scrollTo(
0,
STATE.stream_element.parentElement.scrollHeight,
);
}
function sock_con() {
const socket = new WebSocket(
`//${window.location.origin.split("//")[1]}/api/v1/chats/${STATE.chat_id}/_connect`,
);
socket.addEventListener("message", async (event) => {
if (event.data === "Ping") {
return socket.send("Pong");
}
const msg = JSON.parse(event.data);
if (msg.method === "MessageCreate") {
render_message(msg.body);
setTimeout(() => {
clear_notifications();
}, 1000);
} else if (msg.method === "MessageDelete") {
if (document.getElementById(`message_${msg.body}`)) {
document.getElementById(`message_${msg.body}`).remove();
}
if (document.getElementById(`message_${msg.body}_reply`)) {
document.getElementById(`message_${msg.body}_reply`).remove();
}
} else if (msg.method === "MessageUpdate") {
const [id, content] = JSON.parse(msg.body);
document.getElementById(`${id}_body`).innerHTML =
await render_markdown(content);
} else if (msg.method === "ReadReceipt") {
setTimeout(() => {
read_receipt();
}, 150);
}
});
}
function create_message(e) {
e.preventDefault();
const body = new FormData();
// attach images
if (e.target.images) {
for (const file of e.target.images.files) {
body.append(file.name, file);
}
}
// add json body
body.append(
"body",
JSON.stringify({
content: e.target.content.value,
replying_to: STATE.replying_to,
}),
);
// send request
fetch(`/api/v1/messages/${STATE.chat_id}`, { method: "POST", body })
.then((res) => res.json())
.then((res) => {
if (res.ok) {
e.target.reset();
document.getElementById("images_zone").classList.add("hidden");
clear_replying_to();
} else {
show_message(res.message, res.ok);
}
});
}
function mark_message_read() {
fetch(`/api/v1/chats/${STATE.chat_id}/read_message`, {
method: "POST",
})
.then((res) => res.json())
.then((res) => {
if (!res.ok) {
show_message(res.message, res.ok);
}
});
}
function read_receipt() {
if (new Date().getTime() - STATE.last_read_receipt_load < 150) {
console.log("too soon");
return;
}
STATE.last_read_receipt_load = new Date().getTime();
if (document.getElementById("delivered_read_status")) {
document.getElementById("delivered_read_status").remove();
}
fetch(`/chats/_templates/chat/${STATE.chat_id}/read_receipt`)
.then((res) => res.text())
.then((res) => {
document.getElementById("read_receipt_zone").innerHTML = res;
});
}
function delete_message(id) {
if (!confirm("Are you sure you want to do this?")) {
return;
}
fetch(`/api/v1/messages/${id}`, {
method: "DELETE",
})
.then((res) => res.json())
.then((res) => {
if (!res.ok) {
show_message(res.message, res.ok);
}
});
}
async function render_markdown(content) {
return await (
await fetch("/api/v1/markdown", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ content }),
})
).text();
}
function edit_message_ui(id) {
document.getElementById(`${id}_body`).classList.add("hidden");
document.getElementById(`${id}_edit_area`).classList.remove("hidden");
}
function edit_message(id, e) {
e.preventDefault();
fetch(`/api/v1/messages/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
content: e.target.content.value,
}),
})
.then((res) => res.json())
.then(async (res) => {
if (!res.ok) {
show_message(res.message, res.ok);
} else {
document
.getElementById(`${id}_body`)
.classList.remove("hidden");
document
.getElementById(`${id}_edit_area`)
.classList.add("hidden");
}
});
}
function leave_chat(id) {
if (!confirm("Are you sure you would like to do this?")) {
return;
}
fetch(`/api/v1/chats/${id}/leave`, {
method: "POST",
})
.then((res) => res.json())
.then((res) => {
show_message(res.message, res.ok);
});
}
function add_member_to_chat(e, chat_id) {
e.preventDefault();
document.getElementById("add_user_dialog").close();
fetch(`/api/v1/chats/${chat_id}/members/add/${e.target.username.value}`, {
method: "POST",
})
.then((res) => res.json())
.then((res) => {
show_message(res.message, res.ok);
});
}
function update_chat_info(id, info) {
fetch(`/api/v1/chats/${id}/info`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
info,
}),
})
.then((res) => res.json())
.then((res) => {
show_message(res.message, res.ok);
});
}
function rename_chat(id, info) {
const new_name = prompt("New name:");
if (!new_name) {
return;
}
info.name = new_name;
update_chat_info(id, info);
}
function remove_member_from_chat(chat_id, uid) {
if (!confirm("Are you sure you would like to do this?")) {
return;
}
fetch(`/api/v1/chats/${chat_id}/members/remove/${uid}`, {
method: "POST",
})
.then((res) => res.json())
.then((res) => {
show_message(res.message, res.ok);
});
}
function create_direct_chat_with_user(id) {
fetch(`/api/v1/chats`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
style: "Direct",
members: [id],
}),
})
.then((res) => res.json())
.then((res) => {
if (!res.ok) {
show_message(res.message, res.ok);
} else {
window.location.href = `/chats/${res.payload}`;
}
});
}
function pin_message(e, id) {
fetch(`/api/v1/chats/${STATE.chat_id}/pins/${id}`, {
method: "POST",
})
.then((res) => res.json())
.then((res) => {
show_message(res.message, res.ok);
if (res.ok) {
e.target.remove();
}
});
}
function unpin_message(id) {
fetch(`/api/v1/chats/${STATE.chat_id}/pins/${id}`, {
method: "DELETE",
})
.then((res) => res.json())
.then((res) => {
show_message(res.message, res.ok);
});
}
function display_pending_images(e) {
document.getElementById("images_zone").innerHTML = "";
document.getElementById("images_zone").classList.remove("hidden");
if (e.target.files.length < 1) {
document.getElementById("images_zone").classList.add("hidden");
return;
}
let idx = 0;
for (const file of e.target.files) {
document.getElementById("images_zone").innerHTML +=
`<button class="button surface" onclick="remove_file_from_picker('images', ${idx})">${file.name}</button>`;
idx += 1;
}
}
function remove_file_from_picker(input_id, idx) {
const input = document.getElementById(input_id);
const files = Array.from(input.files);
files.splice(idx - 1, 1);
// update files
const list = new DataTransfer();
for (item of files) {
list.items.add(item);
}
input.files = list.files;
// render
display_pending_images({ target: input });
}
function reply_to_message(id) {
STATE.replying_to = id;
document.getElementById("replying_to_zone").classList.remove("hidden");
document.getElementById(`message_${id}`).classList.add("card");
document.getElementById(`message_${id}`).classList.add("surface");
scroll_bottom();
}
function clear_replying_to() {
if (STATE.replying_to) {
document
.getElementById(`message_${STATE.replying_to}`)
.classList.remove("card");
document
.getElementById(`message_${STATE.replying_to}`)
.classList.remove("surface");
}
STATE.replying_to = undefined;
document.getElementById("replying_to_zone").classList.add("hidden");
}
function clear_notifications() {
fetch(`/api/v1/chats/${STATE.chat_id}/notifications`, {
method: "DELETE",
})
.then((res) => res.json())
.then((res) => {
if (!res.ok) {
show_message(res.message, res.ok);
}
});
}
function mute_chat() {
fetch(`/api/v1/chats/${STATE.chat_id}/mute`, {
method: "POST",
})
.then((res) => res.json())
.then((res) => {
show_message(res.message, res.ok);
});
}
function unmute_chat() {
fetch(`/api/v1/chats/${STATE.chat_id}/mute`, {
method: "DELETE",
})
.then((res) => res.json())
.then((res) => {
show_message(res.message, res.ok);
});
}