130 lines
4.4 KiB
TypeScript
130 lines
4.4 KiB
TypeScript
import { useEffect } from "preact/hooks";
|
|
|
|
export default function TenantMessageThreads() {
|
|
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);
|
|
|
|
// API call to mark as read
|
|
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, action: "mark_read" }), // Explicitly pass action
|
|
});
|
|
|
|
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) {
|
|
// Revert state if update failed
|
|
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) {
|
|
// Hide the row if we are on the 'unread' filter view
|
|
row.classList.add("hidden");
|
|
}
|
|
}
|
|
|
|
// Focus on the reply textarea if it exists
|
|
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" />;
|
|
}
|