const messageIdentifierPrefix = `mintership-forum-message`; const messageAttachmentIdentifierPrefix = `mintership-forum-attachment`; // NOTE - SET adminGroups in QortalApi.js to enable admin access to forum for specific groups. Minter Admins will be fetched automatically. let replyToMessageIdentifier = null; let latestMessageIdentifiers = {}; // To keep track of the latest message in each room let currentPage = 0; // Track current pagination page let existingIdentifiers = new Set(); // Keep track of existing identifiers to not pull them more than once. // If there is a previous latest message identifiers, use them. Otherwise, use an empty. if (localStorage.getItem("latestMessageIdentifiers")) { latestMessageIdentifiers = JSON.parse(localStorage.getItem("latestMessageIdentifiers")); } document.addEventListener("DOMContentLoaded", async () => { // Identify the link for 'Mintership Forum' const mintershipForumLinks = document.querySelectorAll('a[href="MINTERSHIP-FORUM"]'); mintershipForumLinks.forEach(link => { link.addEventListener('click', async (event) => { event.preventDefault(); await login(); // Assuming login is an async function await loadForumPage(); loadRoomContent("general"); // Automatically load General Room on forum load startPollingForNewMessages(); // Start polling for new messages after loading the forum page }); }); }); async function loadForumPage() { // Remove all sections except the menu const allSections = document.querySelectorAll('body > section'); allSections.forEach(section => { if (!section.classList.contains('menu')) { section.remove(); } }); // Check if user is an admin // const minterGroupAdmins = await fetchMinterGroupAdmins(); // const isUserAdmin = minterGroupAdmins.members.some(admin => admin.member === userState.accountAddress && admin.isAdmin) || await verifyUserIsAdmin(); // Create the forum layout, including a header, sub-menu, and keeping the original background imagestyle="background-image: url('/assets/images/background.jpg');"> const mainContent = document.createElement('div'); mainContent.innerHTML = `
No messages found. Be the first to post!
`; } return; } // Define `mostRecentMessage` to track the latest message during this fetch let mostRecentMessage = latestMessageIdentifiers[room]?.latestTimestamp ? latestMessageIdentifiers[room] : null; // Fetch all messages that haven't been fetched before const fetchMessages = await Promise.all(response.map(async (resource) => { if (existingIdentifiers.has(resource.identifier)) { return null; // Skip messages that are already displayed } try { console.log(`Fetching message with identifier: ${resource.identifier}`); const messageResponse = await qortalRequest({ action: "FETCH_QDN_RESOURCE", name: resource.name, service: "BLOG_POST", identifier: resource.identifier, }); console.log("Fetched message response:", messageResponse); // No need to decode, as qortalRequest returns the decoded data if no 'encoding: base64' is set. const messageObject = messageResponse; const timestamp = resource.updated || resource.created; const formattedTimestamp = await timestampToHumanReadableDate(timestamp); return { name: resource.name, content: messageObject.messageHtml, date: formattedTimestamp, identifier: resource.identifier, replyTo: messageObject.replyTo, timestamp, attachments: messageObject.attachments || [] // Include attachments if they exist }; } catch (error) { console.error(`Failed to fetch message with identifier ${resource.identifier}. Error: ${error.message}`); return null; } })); // Render new messages without duplication for (const message of fetchMessages) { if (message && !existingIdentifiers.has(message.identifier)) { let replyHtml = ""; if (message.replyTo) { const repliedMessage = fetchMessages.find(m => m && m.identifier === message.replyTo); if (repliedMessage) { replyHtml = ` `; } } const isNewMessage = !mostRecentMessage || new Date(message.timestamp) > new Date(mostRecentMessage?.latestTimestamp); let attachmentHtml = ""; if (message.attachments && message.attachments.length > 0) { for (const attachment of message.attachments) { if (attachment.mimeType.startsWith('image/')) { try { // OTHER METHOD NOT BEING USED HERE. WE CAN LOAD THE IMAGE DIRECTLY SINCE IT WILL BE PUBLISHED UNENCRYPTED/UNENCODED. // const imageHtml = await loadImageHtml(attachment.service, attachment.name, attachment.identifier, attachment.filename, attachment.mimeType); const imageUrl = `/arbitrary/${attachment.service}/${attachment.name}/${attachment.identifier}`; // Add the image HTML with the direct URL attachmentHtml += ` `; // FOR OTHER METHOD NO LONGER USED // attachmentHtml += imageHtml; } catch (error) { console.error(`Failed to fetch attachment ${attachment.filename}:`, error); } } else { // Display a button to download other attachments attachmentHtml += ` `; } } } const messageHTML = ` `; // Append new message to the end of the container messagesContainer.insertAdjacentHTML('beforeend', messageHTML); // Update mostRecentMessage if this message is newer if (!mostRecentMessage || new Date(message.timestamp) > new Date(mostRecentMessage?.latestTimestamp || 0)) { mostRecentMessage = { latestIdentifier: message.identifier, latestTimestamp: message.timestamp }; } // Add the identifier to the existingIdentifiers set existingIdentifiers.add(message.identifier); } } // Update latestMessageIdentifiers for the room if (mostRecentMessage) { latestMessageIdentifiers[room] = mostRecentMessage; localStorage.setItem("latestMessageIdentifiers", JSON.stringify(latestMessageIdentifiers)); } // Add event listeners to the reply buttons const replyButtons = document.querySelectorAll(".reply-button"); replyButtons.forEach(button => { button.addEventListener("click", () => { replyToMessageIdentifier = button.dataset.messageIdentifier; // Find the message being replied to const repliedMessage = fetchMessages.find(m => m && m.identifier === replyToMessageIdentifier); if (repliedMessage) { const replyContainer = document.createElement("div"); replyContainer.className = "reply-container"; replyContainer.innerHTML = ` `; if (!document.querySelector(".reply-container")) { const messageInputSection = document.querySelector(".message-input-section"); if (messageInputSection) { messageInputSection.insertBefore(replyContainer, messageInputSection.firstChild); // Add a listener for the cancel reply button document.getElementById("cancel-reply").addEventListener("click", () => { replyToMessageIdentifier = null; replyContainer.remove(); }); } } const messageInputSection = document.querySelector(".message-input-section"); const editor = document.querySelector(".ql-editor"); if (messageInputSection) { messageInputSection.scrollIntoView({ behavior: 'smooth', block: 'center' }); } if (editor) { editor.focus(); } } }); }); // Render pagination controls const totalMessages = await searchAllCountOnly(`${messageIdentifierPrefix}-${room}`); renderPaginationControls(room, totalMessages, limit); } catch (error) { console.error('Error loading messages from QDN:', error); } } // Polling function to check for new messages without clearing existing ones function startPollingForNewMessages() { setInterval(async () => { const activeRoom = document.querySelector('.room-title')?.innerText.toLowerCase().split(" ")[0]; if (activeRoom) { await loadMessagesFromQDN(activeRoom, currentPage, true); } }, 20000); }