integratedhub/islands/LandlordMessageThreads.tsx
2026-01-20 09:02:26 -06:00

123 lines
4.1 KiB
TypeScript

import { useEffect } from "preact/hooks";
export default function LandlordMessageThreads() {
useEffect(() => {
const container = document.querySelector<HTMLElement>(
"[data-message-form]",
);
if (!container) return;
const statusFilter = container.getAttribute("data-status-filter") || "all";
const e2eUser = container.getAttribute("data-e2e-user") || "";
const toggles = Array.from(
container.querySelectorAll<HTMLElement>("[data-message-toggle]"),
);
const handleToggle = async (event: Event) => {
const button = event.currentTarget as HTMLElement | null;
if (!button) return;
const row = button.closest<HTMLElement>("[data-message-row]");
const panel = row?.querySelector<HTMLElement>("[data-message-panel]");
if (!panel) return;
const subject = row?.querySelector<HTMLElement>(
"[data-message-subject]",
);
const preview = row?.querySelector<HTMLElement>(
"[data-message-preview]",
);
const updateTextStyles = (isRead: boolean) => {
if (subject) {
subject.classList.toggle("font-semibold", !isRead);
subject.classList.toggle("text-gray-900", !isRead);
subject.classList.toggle("font-medium", isRead);
subject.classList.toggle("text-gray-700", isRead);
}
if (preview) {
preview.classList.toggle("text-gray-700", !isRead);
preview.classList.toggle("text-gray-500", isRead);
}
};
const nextOpen = panel.classList.contains("hidden");
panel.classList.toggle("hidden", !nextOpen);
button.setAttribute("aria-expanded", String(nextOpen));
if (nextOpen) {
const messageId = button.getAttribute("data-message-id");
const isUnread = button.getAttribute("data-message-unread") === "true";
if (messageId && isUnread) {
button.setAttribute("data-message-unread", "false");
if (row) {
row.setAttribute("data-message-status", "read");
}
const badge = row?.querySelector<HTMLElement>("[data-unread-pill]");
if (badge) badge.classList.add("hidden");
const dot = row?.querySelector<HTMLElement>("[data-unread-dot]");
if (dot) dot.classList.add("hidden");
updateTextStyles(true);
const url = new URL("/api/update/message", window.location.origin);
if (e2eUser) {
url.searchParams.set("e2e-user", e2eUser);
}
const response = await fetch(url.toString(), {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
},
credentials: "include",
cache: "no-store",
body: JSON.stringify({ id: messageId }),
});
let updateSucceeded = response.ok;
if (response.ok) {
try {
const payload = await response.json();
if (
payload && typeof payload.updated_count === "number" &&
payload.updated_count === 0
) {
updateSucceeded = false;
}
} catch (_) {
updateSucceeded = false;
}
}
if (!updateSucceeded) {
button.setAttribute("data-message-unread", "true");
if (row) {
row.setAttribute("data-message-status", "unread");
}
badge?.classList.remove("hidden");
dot?.classList.remove("hidden");
updateTextStyles(false);
} else if (statusFilter === "unread" && row) {
row.classList.add("hidden");
}
}
const textarea = row?.querySelector<HTMLTextAreaElement>(
"textarea[name='content']",
);
textarea?.focus();
}
};
toggles.forEach((toggle) => {
toggle.addEventListener("click", handleToggle);
});
return () => {
toggles.forEach((toggle) => {
toggle.removeEventListener("click", handleToggle);
});
};
}, []);
return <span class="hidden" data-message-threads aria-hidden="true" />;
}