diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cac48f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.vscode +/.sync* diff --git a/BACKUP/index-Dec-9-afterRemovingBottomSection.html b/BACKUP/index-Dec-9-afterRemovingBottomSection.html deleted file mode 100644 index e0585dc..0000000 --- a/BACKUP/index-Dec-9-afterRemovingBottomSection.html +++ /dev/null @@ -1,260 +0,0 @@ - - -
- - - - - - - - - -This is the initial 'alpha' of the Mintership Forum / Mintership tools that will be built into the final Q-Mintership app. This is a simplistic version built by crowetic that will offer a very simple communciations location, and the tools for the minter admins to accomplish the necessary GROUP_APPROVAL transactions. Scroll down for the currently available tools...
- -- Learn more about the Mintership concept, and why it was needed. The days of 'sponsorship' are a thing of the past on the Qortal Network. No more will there be the ability to self-sponsor. A new era of Qortal begins! Join the conversation with the other minters and admins here!
- -- Not already minting? You've come to the right place to get started. The 'MINTERS.' links will take you to the 'Minter Board'. The Minter Board is a place to publish your intent to become a minter, and get support from the existing minters and Minter Admins.
- -- Are you one of the initially selected Minter Admins? We have the tools here you need to create and approve GROUP_APPROVAL transactions, and communicate securely with your fellow admins. There is a private forum, and Minter Admin Tools section available for you!
- -- This is the very start of the Q-Mintership app. It will be dramatically changing upon the beta release, and modification to the Q-Mintership Q-App. This initial version is a version that could be launched more quickly, and does not have nearly as much functionality as what will exist once the main app goes live.
-This is the initial 'alpha' of the Mintership Forum / Mintership tools that will be built into the final Q-Mintership app. This is a simplistic version built by crowetic that will offer a very simple communciations location, and the tools for the minter admins to accomplish the necessary GROUP_APPROVAL transactions. Scroll down for the currently available tools...
- -- Learn more about the Mintership concept, and why it was needed. The days of 'sponsorship' are a thing of the past on the Qortal Network. No more will there be the ability to self-sponsor. A new era of Qortal begins!
-- Not already minting? You've come to the right place to get started. The 'MINTERS.' links will take you to the 'Minter Board'. The Minter Board is a place to publish your intent to become a minter, and get support from the existing minters and Minter Admins.
- -- Are you one of the initially selected Minter Admins? We have the tools here you need to create and approve GROUP_APPROVAL transactions, and communicate securely with your fellow admins.
-- Would you like to become a minter? You've come to the right place! Obtain details about the new Mintership-based minting process on Qortal.
-- If you are a Minter Admin, you will need to know how to create and sign GROUP_APPROVAL transactions. You may do so here!
-- Every mission has a beginning. If your mission is to become a minter, or a Minter Admin, then you've landed at the correct launchpad!
-- This is the very start of the Q-Mintership app. It will be dramatically changing upon the beta release, and modification to the Q-Mintership Q-App. This initial version is a version that could be launched more quickly, and does not have nearly as much functionality as what will exist once the main app goes live.
-This page is still under development. Until the final Mintership proposal modifications are made, and the MINTER group is transferred to null, there is no need for this page's functionality. The page will be updated when the final modifications are made.
+This page until then is simply a placeholder.
+Transaction Type: ${tx.type}
-Amount: ${tx.amount}
-Creator Address: ${tx.creatorAddress}
-Recipient: ${tx.recipient}
-Timestamp: ${new Date(tx.timestamp).toLocaleString()}
- -No pending approvals found.
'; - } -} - -// Placeholder function to create a pending group invite -async function createPendingGroupInvite() { - console.log("Creating a pending group invite..."); - // Placeholder code for creating a pending group invite - alert('Pending group invite created (placeholder).'); -} - -// Placeholder function to create a pending promotion -async function createPendingPromotion() { - console.log("Creating a pending promotion..."); - // Placeholder code for creating a pending promotion - alert('Pending promotion created (placeholder).'); -} - -// Placeholder function for approving a transaction -function approveTransaction(signature) { - console.log("Approving transaction with signature: ", signature); - // Placeholder code for approving transaction - alert(`Transaction with signature ${signature} approved (placeholder).`); -} diff --git a/assets/js/BACKUP/AdminTools.js-notworkingwell-nov-29-2024.js b/assets/js/BACKUP/AdminTools.js-notworkingwell-nov-29-2024.js deleted file mode 100644 index 20c690c..0000000 --- a/assets/js/BACKUP/AdminTools.js-notworkingwell-nov-29-2024.js +++ /dev/null @@ -1,157 +0,0 @@ -let currentMinterToolPage = 'overview'; // Track the current page - -// Load latest state for admin verification -async function verifyMinterAdminState() { - const minterGroupAdmins = await fetchMinterGroupAdmins(); - return minterGroupAdmins.members.some(admin => admin.member === userState.accountAddress && admin.isAdmin) -} - -document.addEventListener('DOMContentLoaded', async () => { - const isAdmin = await verifyUserIsAdmin(); - - if (isAdmin) { - console.log(`User is an Admin, buttons for MA Tools not removed. userState.isAdmin = ${userState.isMinterAdmin}`); - } else { - // Remove all "TOOLS" links and their related elements - const toolsLinks = document.querySelectorAll('a[href="TOOLS"]'); - toolsLinks.forEach(link => { - // If the link is within a button, remove the button - const buttonParent = link.closest('button'); - if (buttonParent) { - buttonParent.remove(); - } - - // If the link is within an image card or any other element, remove that element - const cardParent = link.closest('.item.features-image'); - if (cardParent) { - cardParent.remove(); - } - - // Finally, remove the link itself if it's not covered by the above removals - link.remove(); - }); - - console.log(`User is NOT a Minter Admin, buttons for MA Tools removed. userState.isMinterAdmin = ${userState.isMinterAdmin}`); - - // Center the remaining card if it exists - const remainingCard = document.querySelector('.features7 .row .item.features-image'); - if (remainingCard) { - remainingCard.classList.remove('col-lg-6', 'col-md-6'); - remainingCard.classList.add('col-12', 'text-center'); - } - - return; - } - - // Add event listener for admin tools link if the user is an admin - const toolsLinks = document.querySelectorAll('a[href="TOOLS"]'); - toolsLinks.forEach(link => { - link.addEventListener('click', async (event) => { - event.preventDefault(); - await loadMinterAdminToolsPage(); - }); - }); -}); - - -async function loadMinterAdminToolsPage() { - // Remove all sections except the menu - const allSections = document.querySelectorAll('body > section'); - allSections.forEach(section => { - if (!section.classList.contains('menu')) { - section.remove(); - } - }); - - // Set the background image directly from a file - const mainContent = document.createElement('div'); - mainContent.innerHTML = ` -Transaction Type: ${tx.type}
-Amount: ${tx.amount}
-Creator Address: ${tx.creatorAddress}
-Recipient: ${tx.recipient}
-Timestamp: ${new Date(tx.timestamp).toLocaleString()}
- -No pending approvals found.
'; - } -} - -// Placeholder function to create a pending group invite -async function createPendingGroupInvite() { - console.log("Creating a pending group invite..."); - // Placeholder code for creating a pending group invite - alert('Pending group invite created (placeholder).'); -} - -// Placeholder function to create a pending promotion -async function createPendingPromotion() { - console.log("Creating a pending promotion..."); - // Placeholder code for creating a pending promotion - alert('Pending promotion created (placeholder).'); -} - -// Placeholder function for approving a transaction -function approveTransaction(signature) { - console.log("Approving transaction with signature: ", signature); - // Placeholder code for approving transaction - alert(`Transaction with signature ${signature} approved (placeholder).`); -} diff --git a/assets/js/BACKUP/BU-Q-Mintership-ScrollingWeirdness-1841PM-11-18-2024.js b/assets/js/BACKUP/BU-Q-Mintership-ScrollingWeirdness-1841PM-11-18-2024.js deleted file mode 100644 index ec87a77..0000000 --- a/assets/js/BACKUP/BU-Q-Mintership-ScrollingWeirdness-1841PM-11-18-2024.js +++ /dev/null @@ -1,317 +0,0 @@ -const messageIdentifierPrefix = `mintership-forum-message`; - -let replyToMessageIdentifier = null; -let latestMessageIdentifiers = {}; // To keep track of the latest message in each room -let currentPage = 0; // Track current pagination page - -// Load the latest message identifiers from local storage -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'); - // const backgroundImage = document.querySelector('.header1')?.style.backgroundImage; - const backgroundImage = "url('/assets/images/background.jpg')" - mainContent.innerHTML = ` -No messages found. Be the first to post!
`; - } - return; - } - - // Clear messages only when loading the first page - if (page === 0) { - messagesContainer.innerHTML = ""; - } - - // Fetch all messages - const fetchMessages = await Promise.all(qdnMessages.map(async (resource) => { - 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 }; - } catch (error) { - console.error(`Failed to fetch message with identifier ${resource.identifier}. Error: ${error.message}`); - return null; - } - })); - - // Render messages without duplication - const existingIdentifiers = new Set(Array.from(messagesContainer.querySelectorAll('.message-item')).map(item => item.dataset.identifier)); - - fetchMessages.forEach((message) => { - if (message && !existingIdentifiers.has(message.identifier)) { - let replyHtml = ""; - if (message.replyTo) { - const repliedMessage = fetchMessages.find(m => m && m.identifier === message.replyTo); - if (repliedMessage) { - replyHtml = ` -No messages found. Be the first to post!
`; - } - return; - } - - // Clear messages only when loading the first page - if (page === 0) { - messagesContainer.innerHTML = ""; - } - - // Fetch all messages - const fetchMessages = await Promise.all(qdnMessages.map(async (resource) => { - 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 }; - } catch (error) { - console.error(`Failed to fetch message with identifier ${resource.identifier}. Error: ${error.message}`); - return null; - } - })); - - // Render messages without duplication - const existingIdentifiers = new Set(Array.from(messagesContainer.querySelectorAll('.message-item')).map(item => item.dataset.identifier)); - - fetchMessages.forEach((message) => { - if (message && !existingIdentifiers.has(message.identifier)) { - let replyHtml = ""; - if (message.replyTo) { - const repliedMessage = fetchMessages.find(m => m && m.identifier === message.replyTo); - if (repliedMessage) { - replyHtml = ` -No messages found. Be the first to post!
`; - } - return; - } - - // Define `mostRecentMessage` to track the latest message during this fetch - let mostRecentMessage = null; - - // Fetch all messages that haven't been fetched before - const fetchMessages = await Promise.all(response.map(async (resource) => { - 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 = ` -No messages found. Be the first to post!
`; - } - return; - } - - // Define `mostRecentMessage` to track the latest message during this fetch - let mostRecentMessage = null; - - // Fetch all messages that haven't been fetched before - const fetchMessages = await Promise.all(response.map(async (resource) => { - 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 = ` -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 = ` -No messages found. Be the first to post!
`; - } - return; - } - - if (!isPolling) { - messagesContainer.innerHTML = ""; - } - - // Define `mostRecentMessage` to track the latest message during this fetch - let mostRecentMessage = null; - - // Fetch all messages that haven't been fetched before - const fetchMessages = await Promise.all(response.map(async (resource) => { - 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 = ` -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 = ` -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 = ` -Loading cards...
"; - - try { - const response = await qortalRequest({ - action: "SEARCH_QDN_RESOURCES", - service: "BLOG_POST", - identifierPrefix: "minter-board-card-", - }); - - if (!response || response.length === 0) { - cardsContainer.innerHTML = "No cards found.
"; - return; - } - - cardsContainer.innerHTML = ""; - for (const card of response) { - const cardDataResponse = await qortalRequest({ - action: "FETCH_QDN_RESOURCE", - name: card.name, - service: "BLOG_POST", - identifier: card.identifier, - }); - - const cardData = JSON.parse(atob(cardDataResponse)); - const cardHTML = createCardHTML(cardData); - cardsContainer.insertAdjacentHTML("beforeend", cardHTML); - } - } catch (error) { - console.error("Error loading cards:", error); - cardsContainer.innerHTML = "Failed to load cards.
"; - } -} - -function createCardHTML(cardData) { - const { header, content, links, creator, timestamp } = cardData; - const formattedDate = new Date(timestamp).toLocaleString(); - const linksHTML = links.map(link => `${link}`).join("${content}
-Published by: ${creator} on ${formattedDate}
-No messages found. Be the first to post!
`; - } - return; - } - - let messagesHTML = messagesContainer.innerHTML; - - const fetchMessages = await Promise.all(qdnMessages.map(async (resource) => { - 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 }; - } catch (error) { - console.error(`Failed to fetch message with identifier ${resource.identifier}. Error: ${error.message}`); - return null; - } - })); - - fetchMessages.forEach(async (message) => { - if (message) { - let replyHtml = ""; - if (message.replyTo) { - const repliedMessage = fetchMessages.find(m => m && m.identifier === message.replyTo); - if (repliedMessage) { - replyHtml = ` -No messages found. Be the first to post!
`; - return; - } - - let messagesHTML = ""; - - const fetchMessages = await Promise.all(qdnMessages.map(async (resource) => { - 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 }; - } catch (error) { - console.error(`Failed to fetch message with identifier ${resource.identifier}. Error: ${error.message}`); - return null; - } - })); - - fetchMessages.forEach(async (message) => { - if (message) { - let replyHtml = ""; - if (message.replyTo) { - const repliedMessage = fetchMessages.find(m => m && m.identifier === message.replyTo); - if (repliedMessage) { - replyHtml = ` -No messages found. Be the first to post!
`; - return; - } - - let messagesHTML = ""; - - const fetchMessages = await Promise.all(qdnMessages.map(async (resource) => { - 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 - const messageObject = messageResponse; - const timestamp = resource.updated || resource.created; - const formattedTimestamp = await timestampToHumanReadableDate(timestamp); - return { name: resource.name, content: messageObject.messageHtml, date: formattedTimestamp }; - } catch (error) { - console.error(`Failed to fetch message with identifier ${resource.identifier}. Error: ${error.message}`); - return null; - } - })); - - fetchMessages.forEach(async (message) => { - if (message) { - messagesHTML += ` -No messages found. Be the first to post!
`; - return; - } - - let messagesHTML = ""; - - const fetchMessages = await Promise.all(qdnMessages.map(async (resource) => { - 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 }; - } catch (error) { - console.error(`Failed to fetch message with identifier ${resource.identifier}. Error: ${error.message}`); - return null; - } - })); - - fetchMessages.forEach(async (message) => { - if (message) { - let replyHtml = ""; - if (message.replyTo) { - const repliedMessage = fetchMessages.find(m => m && m.identifier === message.replyTo); - if (repliedMessage) { - replyHtml = ` -No messages found. Be the first to post!
`; - return; - } - - let messagesHTML = ""; - - const fetchMessages = await Promise.all(qdnMessages.map(async (resource) => { - 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 - 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 }; - } catch (error) { - console.error(`Failed to fetch message with identifier ${resource.identifier}. Error: ${error.message}`); - return null; - } - })); - - fetchMessages.forEach(async (message) => { - if (message) { - let replyHtml = ""; - if (message.replyTo) { - const repliedMessage = fetchMessages.find(m => m && m.identifier === message.replyTo); - if (repliedMessage) { - replyHtml = ` -No messages found. Be the first to post!
`; - return; - } - - let messagesHTML = ""; - - const fetchMessages = await Promise.all(qdnMessages.map(async (resource) => { - 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 - 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 }; - } catch (error) { - console.error(`Failed to fetch message with identifier ${resource.identifier}. Error: ${error.message}`); - return null; - } - })); - - fetchMessages.forEach(async (message) => { - if (message) { - let replyHtml = ""; - if (message.replyTo) { - const repliedMessage = fetchMessages.find(m => m && m.identifier === message.replyTo); - if (repliedMessage) { - replyHtml = ` -Loading cards...
"; - - try { - const response = await qortalRequest({ - action: "SEARCH_QDN_RESOURCES", - service: "BLOG_POST", - query: cardIdentifierPrefix, - }); - - if (!response || response.length === 0) { - cardsContainer.innerHTML = "No cards found.
"; - return; - } - - cardsContainer.innerHTML = ""; - const pollResultsCache = {}; - - for (const card of response) { - const cardDataResponse = await qortalRequest({ - action: "FETCH_QDN_RESOURCE", - name: card.name, - service: "BLOG_POST", - identifier: card.identifier, - }); - - const cardData = cardDataResponse; - // Cache poll results - if (!pollResultsCache[cardData.poll]) { - pollResultsCache[cardData.poll] = await fetchPollResults(cardData.poll); - } - - const pollResults = pollResultsCache[cardData.poll]; - const cardHTML = await createCardHTML(cardData, pollResults); - cardsContainer.insertAdjacentHTML("beforeend", cardHTML); - } - } catch (error) { - console.error("Error loading cards:", error); - cardsContainer.innerHTML = "Failed to load cards.
"; - } -} - -const calculatePollResults = (pollData, minterGroupMembers) => { - const memberAddresses = minterGroupMembers.map(member => member.member); - let adminYes = 0, adminNo = 0, minterYes = 0, minterNo = 0; - - pollData.votes.forEach(vote => { - const voterAddress = vote.voterPublicKey; - const isAdmin = minterGroupMembers.some(member => member.member === voterAddress && member.isAdmin); - - if (vote.optionIndex === 1) { - isAdmin ? adminYes++ : memberAddresses.includes(voterAddress) ? minterYes++ : null; - } else if (vote.optionIndex === 0) { - isAdmin ? adminNo++ : memberAddresses.includes(voterAddress) ? minterNo++ : null; - } - }); - - const totalYes = adminYes + minterYes; - const totalNo = adminNo + minterNo; - - return { adminYes, adminNo, minterYes, minterNo, totalYes, totalNo }; -}; - -const postComment = async (cardIdentifier) => { - const commentInput = document.getElementById(`new-comment-${cardIdentifier}`); - const commentText = commentInput.value.trim(); - if (!commentText) { - alert('Comment cannot be empty!'); - return; - } - - const commentData = { - content: commentText, - creator: userState.accountName, - timestamp: Date.now(), - }; - - const commentIdentifier = `${cardIdentifier}-comment-${await uid()}`; - - try { - const base64CommentData = await objectToBase64(commentData); - if (!base64CommentData) { - console.log(`initial base64 object creation with objectToBase64 failed, using btoa...`); - base64CommentData = btoa(JSON.stringify(commentData)); - } - // const base64CommentData = btoa(JSON.stringify(commentData)); - await qortalRequest({ - action: 'PUBLISH_QDN_RESOURCE', - name: userState.accountName, - service: 'BLOG_POST', - identifier: commentIdentifier, - data64: base64CommentData, - }); - alert('Comment posted successfully!'); - commentInput.value = ''; // Clear input - await displayComments(cardIdentifier); // Refresh comments - } catch (error) { - console.error('Error posting comment:', error); - alert('Failed to post comment.'); - } -}; - -const fetchCommentsForCard = async (cardIdentifier) => { - try { - const response = await qortalRequest({ - action: 'SEARCH_QDN_RESOURCES', - service: 'BLOG_POST', - query: `${cardIdentifier}-comment`, - }); - return response; - } catch (error) { - console.error(`Error fetching comments for ${cardIdentifier}:`, error); - return []; - } -}; - -const displayComments = async (cardIdentifier) => { - const comments = await fetchCommentsForCard(cardIdentifier); - const commentsContainer = document.getElementById(`comments-container-${cardIdentifier}`); - commentsContainer.innerHTML = comments.map(comment => ` -Loading cards...
"; - - try { - const response = await qortalRequest({ - action: "SEARCH_QDN_RESOURCES", - service: "BLOG_POST", - query: cardIdentifierPrefix, - }); - - if (!response || response.length === 0) { - cardsContainer.innerHTML = "No cards found.
"; - return; - } - - cardsContainer.innerHTML = ""; - const pollResultsCache = {}; - - for (const card of response) { - const cardDataResponse = await qortalRequest({ - action: "FETCH_QDN_RESOURCE", - name: card.name, - service: "BLOG_POST", - identifier: card.identifier, - }); - - const cardData = cardDataResponse; - // Cache poll results - if (!pollResultsCache[cardData.poll]) { - pollResultsCache[cardData.poll] = await fetchPollResults(cardData.poll); - } - - const pollResults = pollResultsCache[cardData.poll]; - const cardHTML = await createCardHTML(cardData, pollResults); - cardsContainer.insertAdjacentHTML("beforeend", cardHTML); - } - } catch (error) { - console.error("Error loading cards:", error); - cardsContainer.innerHTML = "Failed to load cards.
"; - } -} - -function toggleFullContent(cardIdentifier, fullContent) { - const contentPreview = document.getElementById(`content-preview-${cardIdentifier}`); - const toggleButton = document.getElementById(`toggle-content-${cardIdentifier}`); - const isExpanded = contentPreview.getAttribute("data-expanded") === "true"; - - if (isExpanded) { - // Collapse the content - contentPreview.innerText = `${fullContent.substring(0, 150)}...`; - toggleButton.innerText = "Display Full Text"; - contentPreview.setAttribute("data-expanded", "false"); - } else { - // Expand the content - contentPreview.innerText = fullContent; - toggleButton.innerText = "Show Less"; - contentPreview.setAttribute("data-expanded", "true"); - } -} - -async function createCardHTML(cardData, pollResults) { - const { header, content, links, creator, timestamp, poll } = cardData; - const formattedDate = new Date(timestamp).toLocaleString(); - const linksHTML = links.map((link, index) => ` - - `).join(""); - - const minterGroupMembers = await fetchMinterGroupMembers(); - const { adminYes, adminNo, minterYes, minterNo, totalYes, totalNo } = - calculatePollResults(pollResults, minterGroupMembers); - - const trimmedContent = content.length > 150 ? `${content.substring(0, 150)}...` : content; - - return ` -${header}
-Published by: ${creator} on ${formattedDate}
-Loading cards...
"; - - try { - const response = await qortalRequest({ - action: "SEARCH_QDN_RESOURCES", - service: "BLOG_POST", - query: cardIdentifierPrefix, - }); - - if (!response || response.length === 0) { - cardsContainer.innerHTML = "No cards found.
"; - return; - } - - cardsContainer.innerHTML = ""; - const pollResultsCache = {}; - - for (const card of response) { - const cardDataResponse = await qortalRequest({ - action: "FETCH_QDN_RESOURCE", - name: card.name, - service: "BLOG_POST", - identifier: card.identifier, - }); - - const cardData = cardDataResponse; - // Cache poll results - if (!pollResultsCache[cardData.poll]) { - pollResultsCache[cardData.poll] = await fetchPollResults(cardData.poll); - } - - const pollResults = pollResultsCache[cardData.poll]; - const cardHTML = await createCardHTML(cardData, pollResults); - cardsContainer.insertAdjacentHTML("beforeend", cardHTML); - } - } catch (error) { - console.error("Error loading cards:", error); - cardsContainer.innerHTML = "Failed to load cards.
"; - } -} - -const calculatePollResults = (pollData, minterGroupMembers) => { - const memberAddresses = minterGroupMembers.map(member => member.member); - let adminYes = 0, adminNo = 0, minterYes = 0, minterNo = 0; - - pollData.votes.forEach(vote => { - const voterAddress = vote.voterPublicKey; - const isAdmin = minterGroupMembers.some(member => member.member === voterAddress && member.isAdmin); - - if (vote.optionIndex === 1) { - isAdmin ? adminYes++ : memberAddresses.includes(voterAddress) ? minterYes++ : null; - } else if (vote.optionIndex === 0) { - isAdmin ? adminNo++ : memberAddresses.includes(voterAddress) ? minterNo++ : null; - } - }); - - const totalYes = adminYes + minterYes; - const totalNo = adminNo + minterNo; - - return { adminYes, adminNo, minterYes, minterNo, totalYes, totalNo }; -}; - -const postComment = async (cardIdentifier) => { - const commentInput = document.getElementById(`new-comment-${cardIdentifier}`); - const commentText = commentInput.value.trim(); - if (!commentText) { - alert('Comment cannot be empty!'); - return; - } - - const commentData = { - content: commentText, - creator: userState.accountName, - timestamp: Date.now(), - }; - - const commentIdentifier = `${cardIdentifier}-comment-${await uid()}`; - - try { - const base64CommentData = await objectToBase64(commentData); - if (!base64CommentData) { - console.log(`initial base64 object creation with objectToBase64 failed, using btoa...`); - base64CommentData = btoa(JSON.stringify(commentData)); - } - // const base64CommentData = btoa(JSON.stringify(commentData)); - await qortalRequest({ - action: 'PUBLISH_QDN_RESOURCE', - name: userState.accountName, - service: 'BLOG_POST', - identifier: commentIdentifier, - data64: base64CommentData, - }); - alert('Comment posted successfully!'); - commentInput.value = ''; // Clear input - await displayComments(cardIdentifier); // Refresh comments - } catch (error) { - console.error('Error posting comment:', error); - alert('Failed to post comment.'); - } -}; - -const fetchCommentsForCard = async (cardIdentifier) => { - try { - const response = await qortalRequest({ - action: 'SEARCH_QDN_RESOURCES', - service: 'BLOG_POST', - query: `${cardIdentifier}-comment`, - }); - return response; - } catch (error) { - console.error(`Error fetching comments for ${cardIdentifier}:`, error); - return []; - } -}; - -const displayComments = async (cardIdentifier) => { - const comments = await fetchCommentsForCard(cardIdentifier); - const commentsContainer = document.getElementById(`comments-container-${cardIdentifier}`); - commentsContainer.innerHTML = comments.map(comment => ` -${comment.creator}:
-${comment.content}
-${timestampToHumanReadableDate(comment.timestamp)}
-Loading cards...
"; - - try { - const response = await qortalRequest({ - action: "SEARCH_QDN_RESOURCES", - service: "BLOG_POST", - query: cardIdentifierPrefix, - }); - - if (!response || response.length === 0) { - cardsContainer.innerHTML = "No cards found.
"; - return; - } - - cardsContainer.innerHTML = ""; - const pollResultsCache = {}; - - for (const card of response) { - const cardDataResponse = await qortalRequest({ - action: "FETCH_QDN_RESOURCE", - name: card.name, - service: "BLOG_POST", - identifier: card.identifier, - }); - - const cardData = cardDataResponse; - // Cache poll results - if (!pollResultsCache[cardData.poll]) { - pollResultsCache[cardData.poll] = await fetchPollResults(cardData.poll); - } - - const pollResults = pollResultsCache[cardData.poll]; - const cardHTML = await createCardHTML(cardData, pollResults); - cardsContainer.insertAdjacentHTML("beforeend", cardHTML); - } - } catch (error) { - console.error("Error loading cards:", error); - cardsContainer.innerHTML = "Failed to load cards.
"; - } -} - -function toggleFullContent(cardIdentifier, fullContent) { - const contentPreview = document.getElementById(`content-preview-${cardIdentifier}`); - const toggleButton = document.getElementById(`toggle-content-${cardIdentifier}`); - - if (contentPreview.innerText.length > 150) { - // Collapse the content - contentPreview.innerText = `${fullContent.substring(0, 150)}...`; - toggleButton.innerText = "Display Full Content"; - } else { - // Expand the content - contentPreview.innerText = fullContent; - toggleButton.innerText = "Show Less"; - } -} - -async function createCardHTML(cardData, pollResults) { - const { header, content, links, creator, timestamp, poll } = cardData; - const formattedDate = new Date(timestamp).toLocaleString(); - const linksHTML = links.map((link, index) => ` - - `).join(""); - - const minterGroupMembers = await fetchMinterGroupMembers(); - const { adminYes, adminNo, minterYes, minterNo, totalYes, totalNo } = - calculatePollResults(pollResults, minterGroupMembers); - - const trimmedContent = content.length > 150 ? `${content.substring(0, 150)}...` : content; - - return ` -${header}
-Published by: ${creator} on ${formattedDate}
-Loading cards...
"; - - try { - const response = await qortalRequest({ - action: "SEARCH_QDN_RESOURCES", - service: "BLOG_POST", - query: cardIdentifierPrefix, - }); - - if (!response || response.length === 0) { - cardsContainer.innerHTML = "No cards found.
"; - return; - } - - cardsContainer.innerHTML = ""; - const pollResultsCache = {}; - - for (const card of response) { - const cardDataResponse = await qortalRequest({ - action: "FETCH_QDN_RESOURCE", - name: card.name, - service: "BLOG_POST", - identifier: card.identifier, - }); - - const cardData = cardDataResponse; - // Cache poll results - if (!pollResultsCache[cardData.poll]) { - pollResultsCache[cardData.poll] = await fetchPollResults(cardData.poll); - } - - const pollResults = pollResultsCache[cardData.poll]; - const cardHTML = await createCardHTML(cardData, pollResults); - cardsContainer.insertAdjacentHTML("beforeend", cardHTML); - } - } catch (error) { - console.error("Error loading cards:", error); - cardsContainer.innerHTML = "Failed to load cards.
"; - } -} - - -const calculatePollResults = (pollData, minterGroupMembers) => { - const memberAddresses = minterGroupMembers.map(member => member.member); - let adminYes = 0, adminNo = 0, minterYes = 0, minterNo = 0; - - pollData.votes.forEach(vote => { - const voterAddress = vote.voterPublicKey; - const isAdmin = minterGroupMembers.some(member => member.member === voterAddress && member.isAdmin); - - if (vote.optionIndex === 1) { - isAdmin ? adminYes++ : memberAddresses.includes(voterAddress) ? minterYes++ : null; - } else if (vote.optionIndex === 0) { - isAdmin ? adminNo++ : memberAddresses.includes(voterAddress) ? minterNo++ : null; - } - }); - - const totalYes = adminYes + minterYes; - const totalNo = adminNo + minterNo; - - return { adminYes, adminNo, minterYes, minterNo, totalYes, totalNo }; -}; - -const fetchCommentsForCard = async (cardIdentifier) => { - try { - const response = await qortalRequest({ - action: 'SEARCH_QDN_RESOURCES', - service: 'BLOG_POST', - identifier: `${cardIdentifier}-comments`, - }); - return response; - } catch (error) { - console.error(`Error fetching comments for ${cardIdentifier}:`, error); - return []; - } -}; - -const displayComments = async (cardIdentifier) => { - const comments = await fetchCommentsForCard(cardIdentifier); - const commentsContainer = document.getElementById(`comments-container-${cardIdentifier}`); - commentsContainer.innerHTML = comments.map(comment => ` -${comment.creator}:
-${comment.content}
-Loading cards...
"; - - try { - const response = await qortalRequest({ - action: "SEARCH_QDN_RESOURCES", - service: "BLOG_POST", - query: cardIdentifierPrefix, - }); - - if (!response || response.length === 0) { - cardsContainer.innerHTML = "No cards found.
"; - return; - } - - cardsContainer.innerHTML = ""; - const pollResultsCache = {}; - - for (const card of response) { - const cardDataResponse = await qortalRequest({ - action: "FETCH_QDN_RESOURCE", - name: card.name, - service: "BLOG_POST", - identifier: card.identifier, - }); - - const cardData = cardDataResponse; - // Cache poll results - if (!pollResultsCache[cardData.poll]) { - pollResultsCache[cardData.poll] = await fetchPollResults(cardData.poll); - } - - const pollResults = pollResultsCache[cardData.poll]; - const cardHTML = await createCardHTML(cardData, pollResults); - cardsContainer.insertAdjacentHTML("beforeend", cardHTML); - } - } catch (error) { - console.error("Error loading cards:", error); - cardsContainer.innerHTML = "Failed to load cards.
"; - } -} - -function toggleFullContent(cardIdentifier, fullContent) { - const contentPreview = document.getElementById(`content-preview-${cardIdentifier}`); - const toggleButton = document.getElementById(`toggle-content-${cardIdentifier}`); - - if (contentPreview.innerText.length > 150) { - // Collapse the content - contentPreview.innerText = `${fullContent.substring(0, 150)}...`; - toggleButton.innerText = "Display Full Content"; - } else { - // Expand the content - contentPreview.innerText = fullContent; - toggleButton.innerText = "Show Less"; - } -} - -async function createCardHTML(cardData, pollResults) { - const { header, content, links, creator, timestamp, poll } = cardData; - const formattedDate = new Date(timestamp).toLocaleString(); - const linksHTML = links.map((link, index) => ` - - `).join(""); - - const minterGroupMembers = await fetchMinterGroupMembers(); - const { adminYes, adminNo, minterYes, minterNo, totalYes, totalNo } = - calculatePollResults(pollResults, minterGroupMembers); - - const trimmedContent = content.length > 150 ? `${content.substring(0, 150)}...` : content; - - return ` -${header}
-Published by: ${creator} on ${formattedDate}
-Loading cards...
"; - - try { - const response = await qortalRequest({ - action: "SEARCH_QDN_RESOURCES", - service: "BLOG_POST", - query: cardIdentifierPrefix, - }); - - if (!response || response.length === 0) { - cardsContainer.innerHTML = "No cards found.
"; - return; - } - - cardsContainer.innerHTML = "" - - for (const card of response) { - const cardDataResponse = await qortalRequest({ - action: "FETCH_QDN_RESOURCE", - name: card.name, - service: "BLOG_POST", - identifier: card.identifier, - }); - - const cardData = cardDataResponse; - // Cache poll results - - const pollResults = await fetchPollResults(cardData.poll) - const cardHTML = await createCardHTML(cardData, pollResults, card.identifier); - cardsContainer.insertAdjacentHTML("beforeend", cardHTML); - } - } catch (error) { - console.error("Error loading cards:", error); - cardsContainer.innerHTML = "Failed to load cards.
"; - } -} - -const calculatePollResults = (pollData, minterGroupMembers) => { - const memberAddresses = minterGroupMembers.map(member => member.member); - let adminYes = 0, adminNo = 0, minterYes = 0, minterNo = 0; - - pollData.votes.forEach(vote => { - const voterAddress = vote.voterPublicKey; - const isAdmin = minterGroupMembers.some(member => member.member === voterAddress && member.isAdmin); - - if (vote.optionIndex === 1) { - isAdmin ? adminYes++ : memberAddresses.includes(voterAddress) ? minterYes++ : null; - } else if (vote.optionIndex === 0) { - isAdmin ? adminNo++ : memberAddresses.includes(voterAddress) ? minterNo++ : null; - } - }); - - const totalYes = adminYes + minterYes; - const totalNo = adminNo + minterNo; - - return { adminYes, adminNo, minterYes, minterNo, totalYes, totalNo }; -}; - -const postComment = async (cardIdentifier) => { - const commentInput = document.getElementById(`new-comment-${cardIdentifier}`); - const commentText = commentInput.value.trim(); - if (!commentText) { - alert('Comment cannot be empty!'); - return; - } - - const commentData = { - content: commentText, - creator: userState.accountName, - timestamp: Date.now(), - }; - - const commentIdentifier = `${cardIdentifier}-comment-${await uid()}`; - - try { - const base64CommentData = await objectToBase64(commentData); - if (!base64CommentData) { - console.log(`initial base64 object creation with objectToBase64 failed, using btoa...`); - base64CommentData = btoa(JSON.stringify(commentData)); - } - // const base64CommentData = btoa(JSON.stringify(commentData)); - await qortalRequest({ - action: 'PUBLISH_QDN_RESOURCE', - name: userState.accountName, - service: 'BLOG_POST', - identifier: commentIdentifier, - data64: base64CommentData, - }); - alert('Comment posted successfully!'); - commentInput.value = ''; // Clear input - await displayComments(cardIdentifier); // Refresh comments - } catch (error) { - console.error('Error posting comment:', error); - alert('Failed to post comment.'); - } -}; - -const fetchCommentsForCard = async (cardIdentifier) => { - try { - const response = await qortalRequest({ - action: 'SEARCH_QDN_RESOURCES', - service: 'BLOG_POST', - query: `${cardIdentifier}-comment`, - }); - return response; - } catch (error) { - console.error(`Error fetching comments for ${cardIdentifier}:`, error); - return []; - } -}; - -const displayComments = async (cardIdentifier) => { - try { - const comments = await fetchCommentsForCard(cardIdentifier); - const commentsContainer = document.getElementById(`comments-container-${cardIdentifier}`); - - // Clear previous comments - commentsContainer.innerHTML = ''; - - // Fetch and display each comment - for (const comment of comments) { - const commentDataResponse = await qortalRequest({ - action: "FETCH_QDN_RESOURCE", - name: comment.name, - service: "BLOG_POST", - identifier: comment.identifier, - }); - const timestamp = await timestampToHumanReadableDate(commentDataResponse.timestamp); - const commentHTML = ` -${commentDataResponse.creator}:
-${commentDataResponse.content}
-${timestamp}
-Loading cards...
"; - - try { - const response = await qortalRequest({ - action: "SEARCH_QDN_RESOURCES", - service: "BLOG_POST", - query: cardIdentifierPrefix, - }); - - if (!response || response.length === 0) { - cardsContainer.innerHTML = "No cards found.
"; - return; - } - - cardsContainer.innerHTML = ""; - const pollResultsCache = {}; - - for (const card of response) { - const cardDataResponse = await qortalRequest({ - action: "FETCH_QDN_RESOURCE", - name: card.name, - service: "BLOG_POST", - identifier: card.identifier, - }); - - const cardData = cardDataResponse; - // Cache poll results - if (!pollResultsCache[cardData.poll]) { - pollResultsCache[cardData.poll] = await fetchPollResults(cardData.poll); - } - - const pollResults = pollResultsCache[cardData.poll]; - const cardHTML = await createCardHTML(cardData, pollResults, card.identifier); - cardsContainer.insertAdjacentHTML("beforeend", cardHTML); - } - } catch (error) { - console.error("Error loading cards:", error); - cardsContainer.innerHTML = "Failed to load cards.
"; - } -} - -function toggleFullContent(cardIdentifier, fullContent) { - const contentPreview = document.getElementById(`content-preview-${cardIdentifier}`); - const toggleButton = document.getElementById(`toggle-content-${cardIdentifier}`); - const isExpanded = contentPreview.getAttribute("data-expanded") === "true"; - - if (isExpanded) { - // Collapse the content - contentPreview.innerText = `${fullContent.substring(0, 150)}...`; - toggleButton.innerText = "Display Full Text"; - contentPreview.setAttribute("data-expanded", "false"); - } else { - // Expand the content - contentPreview.innerText = fullContent; - toggleButton.innerText = "Show Less"; - contentPreview.setAttribute("data-expanded", "true"); - } -} - -async function createCardHTML(cardData, pollResults, cardIdentifier) { - const { header, content, links, creator, timestamp, poll } = cardData; - const formattedDate = new Date(timestamp).toLocaleString(); - const linksHTML = links.map((link, index) => ` - - `).join(""); - - const minterGroupMembers = await fetchMinterGroupMembers(); - const { adminYes, adminNo, minterYes, minterNo, totalYes, totalNo } = - calculatePollResults(pollResults, minterGroupMembers); - - const trimmedContent = content.length > 150 ? `${content.substring(0, 150)}...` : content; - - return ` -${header}
-Published by: ${creator} on ${formattedDate}
-No messages found. Be the first to post!
`; - } - return; - } - - // Fetch all messages that haven't been fetched before - const fetchMessages = await Promise.all(response.map(async (resource) => { - 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 }; - } catch (error) { - console.error(`Failed to fetch message with identifier ${resource.identifier}. Error: ${error.message}`); - return null; - } - })); - - // Render new messages without duplication - fetchMessages.forEach((message) => { - if (message && !existingIdentifiers.has(message.identifier)) { - let replyHtml = ""; - if (message.replyTo) { - const repliedMessage = fetchMessages.find(m => m && m.identifier === message.replyTo); - if (repliedMessage) { - replyHtml = ` -No messages found. Be the first to post!
`; - } - return; - } - - // Fetch all messages - const fetchMessages = await Promise.all(qdnMessages.map(async (resource) => { - 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 }; - } catch (error) { - console.error(`Failed to fetch message with identifier ${resource.identifier}. Error: ${error.message}`); - return null; - } - })); - - // Render messages without duplication - const existingIdentifiers = new Set(Array.from(messagesContainer.querySelectorAll('.message-item')).map(item => item.dataset.identifier)); - - fetchMessages.forEach((message) => { - if (message && !existingIdentifiers.has(message.identifier)) { - let replyHtml = ""; - if (message.replyTo) { - const repliedMessage = fetchMessages.find(m => m && m.identifier === message.replyTo); - if (repliedMessage) { - replyHtml = ` -No valid cards found.
"; return; } + // Sort valid cards by timestamp (oldest to newest) + validCards.sort((a, b) => { + const timestampA = a.updated || a.created || 0; // Use updated or created timestamp + const timestampB = b.updated || b.created || 0; + return timestampA - timestampB; + }); + + // Reverse the sorted order (newest first) + validCards.reverse(); + + // Clear the container before adding cards + cardsContainer.innerHTML = ""; + + // Process and display each card await Promise.all( validCards.map(async card => { try { @@ -165,9 +180,16 @@ const loadCards = async () => { return; } + // Fetch poll results and check admin votes const pollResults = await fetchPollResults(cardData.poll); console.log(`Poll Results Fetched - totalVotes: ${pollResults.totalVotes}`); + // Check if more than 3 admins voted "No" + if (pollResults.adminNo > 3) { + console.log(`Card ${card.identifier} hidden due to more than 3 admin downvotes.`); + return; // Skip this card + } + const cardHTML = await createCardHTML(cardData, pollResults, card.identifier); cardsContainer.insertAdjacentHTML("beforeend", cardHTML); } catch (error) { @@ -182,6 +204,8 @@ const loadCards = async () => { }; + + // Function to check and fech an existing Minter Card if attempting to publish twice ---------------------------------------- const fetchExistingCard = async () => { try { @@ -339,7 +363,11 @@ const publishCard = async () => { //Calculate the poll results passed from other functions with minterGroupMembers and minterAdmins --------------------------- const calculatePollResults = async (pollData, minterGroupMembers, minterAdmins) => { const memberAddresses = minterGroupMembers.map(member => member.member) - const adminAddresses = minterAdmins.map(member => member.member) + const minterAdminAddresses = minterAdmins.map(member => member.member) + const adminGroupsMembers = await fetchAllAdminGroupsMembers() + const groupAdminAddresses = adminGroupsMembers.map(member => member.member) + const adminAddresses = []; + adminAddresses.push(...minterAdminAddresses,...groupAdminAddresses); let adminYes = 0, adminNo = 0, minterYes = 0, minterNo = 0, yesWeight = 0 , noWeight = 0 @@ -408,7 +436,7 @@ const postComment = async (cardIdentifier) => { alert('Comment posted successfully!'); commentInput.value = ''; // Clear input - await displayComments(cardIdentifier); // Refresh comments + // await displayComments(cardIdentifier); // Refresh comments - We don't need to do this as comments will be displayed only after confirmation. } catch (error) { console.error('Error posting comment:', error); alert('Failed to post comment.'); diff --git a/assets/js/Q-Mintership.js b/assets/js/Q-Mintership.js index dc26456..66ad09b 100644 --- a/assets/js/Q-Mintership.js +++ b/assets/js/Q-Mintership.js @@ -1,5 +1,6 @@ const messageIdentifierPrefix = `mintership-forum-message`; const messageAttachmentIdentifierPrefix = `mintership-forum-attachment`; +let adminPublicKeys = [] // NOTE - SET adminGroups in QortalApi.js to enable admin access to forum for specific groups. Minter Admins will be fetched automatically. @@ -42,14 +43,25 @@ const loadForumPage = async () => { } } + if (typeof userState.isAdmin === 'undefined') { + try { + // Fetch and verify the admin status asynchronously + userState.isAdmin = await verifyUserIsAdmin(); + } catch (error) { + console.error('Error verifying admin status:', error); + userState.isAdmin = false; // Default to non-admin if there's an issue + } + } + const avatarUrl = `/arbitrary/THUMBNAIL/${userState.accountName}/qortal_avatar`; + const isAdmin = userState.isAdmin; - // Create the forum layout, including a header, sub-menu, and keeping the original background imagestyle="background-image: url('/assets/images/background.jpg');"> + // Create the forum layout, including a header, sub-menu, and keeping the original background image: style="background-image: url('/assets/images/background.jpg');"> const mainContent = document.createElement('div'); mainContent.innerHTML = `No messages found. Be the first to post!
`; - } - return; - } - - // Fetch all messages - const fetchMessages = await Promise.all(response.map(async (resource) => { - 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 }; - } catch (error) { - console.error(`Failed to fetch message with identifier ${resource.identifier}. Error: ${error.message}`); - return null; - } - })); - - // Render messages without duplication - fetchMessages.forEach((message) => { - if (message && !existingIdentifiers.has(message.identifier)) { - let replyHtml = ""; - if (message.replyTo) { - const repliedMessage = fetchMessages.find(m => m && m.identifier === message.replyTo); - if (repliedMessage) { - replyHtml = ` -No messages found. Be the first to post!
`; - } - return; - } - - // Fetch all messages that haven't been fetched before - const fetchMessages = await Promise.all(response.map(async (resource) => { - 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 }; - } catch (error) { - console.error(`Failed to fetch message with identifier ${resource.identifier}. Error: ${error.message}`); - return null; - } - })); - - // Render new messages without duplication - fetchMessages.forEach((message) => { - if (message && !existingIdentifiers.has(message.identifier)) { - let replyHtml = ""; - if (message.replyTo) { - const repliedMessage = fetchMessages.find(m => m && m.identifier === message.replyTo); - if (repliedMessage) { - replyHtml = ` -
${comment.creator}:
-${comment.content}
-${timestampToHumanReadableDate(comment.timestamp)}
-