// // NOTE - Change isTestMode to false prior to actual release ---- !important - You may also change identifier if you want to not show older cards. const testMode = false; const cardIdentifierPrefix = "Minter-board-card"; let isExistingCard = false; let existingCardData = {}; let existingCardIdentifier = {}; const loadMinterBoardPage = async () => { // Clear existing content on the page const bodyChildren = document.body.children; for (let i = bodyChildren.length - 1; i >= 0; i--) { const child = bodyChildren[i]; if (!child.classList.contains("menu")) { child.remove(); } } // Add the "Minter Board" content const mainContent = document.createElement("div"); const publishButtonColor = '#527c9d' const minterBoardNameColor = '#527c9d' mainContent.innerHTML = `
Publish a Minter Card with Information, and obtain and view the support of the community. Welcome to the Minter Board!
Refreshing cards...
"; await loadCards(); }); document.getElementById("cancel-publish-button").addEventListener("click", async () => { const cardsContainer = document.getElementById("cards-container"); cardsContainer.style.display = "flex"; // Restore visibility const publishCardView = document.getElementById("publish-card-view"); publishCardView.style.display = "none"; // Hide the publish form }); document.getElementById("add-link-button").addEventListener("click", async () => { const linksContainer = document.getElementById("links-container"); const newLinkInput = document.createElement("input"); newLinkInput.type = "text"; newLinkInput.className = "card-link"; newLinkInput.placeholder = "Enter QDN link"; linksContainer.appendChild(newLinkInput); }); document.getElementById("publish-card-form").addEventListener("submit", async (event) => { event.preventDefault(); await publishCard(); }); await loadCards(); } const extractMinterCardsMinterName = async (cardIdentifier) => { // Ensure the identifier starts with the prefix if (!cardIdentifier.startsWith(`${cardIdentifierPrefix}-`)) { throw new Error('Invalid identifier format or prefix mismatch'); } // Split the identifier into parts const parts = cardIdentifier.split('-'); // Ensure the format has at least 3 parts if (parts.length < 3) { throw new Error('Invalid identifier format'); } try { const nameFromIdentifier = await searchSimple('BLOG_POST', cardIdentifier, "", 1) const minterName = await nameFromIdentifier.name return minterName } catch (error) { throw error } } const processMinterCards = async (validMinterCards) => { const latestCardsMap = new Map() // Step 1: Filter and keep the most recent card per identifier validMinterCards.forEach(card => { const timestamp = card.updated || card.created || 0 const existingCard = latestCardsMap.get(card.identifier) if (!existingCard || timestamp > (existingCard.updated || existingCard.created || 0)) { latestCardsMap.set(card.identifier, card) } }) // Step 2: Extract unique cards const uniqueValidCards = Array.from(latestCardsMap.values()) // Step 3: Group by minterName and select the most recent card per minterName const minterNameMap = new Map() for (const card of validMinterCards) { const minterName = await extractMinterCardsMinterName(card.identifier) const existingCard = minterNameMap.get(minterName) const cardTimestamp = card.updated || card.created || 0 const existingTimestamp = existingCard?.updated || existingCard?.created || 0 // Keep only the most recent card for each minterName if (!existingCard || cardTimestamp > existingTimestamp) { minterNameMap.set(minterName, card) } } // Step 4: Filter cards to ensure each minterName is included only once const finalCards = [] const seenMinterNames = new Set() for (const [minterName, card] of minterNameMap.entries()) { if (!seenMinterNames.has(minterName)) { finalCards.push(card) seenMinterNames.add(minterName) // Mark the minterName as seen } } // Step 5: Sort by the most recent timestamp finalCards.sort((a, b) => { const timestampA = a.updated || a.created || 0 const timestampB = b.updated || b.created || 0 return timestampB - timestampA }) return finalCards } //Main function to load the Minter Cards ---------------------------------------- const loadCards = async () => { const cardsContainer = document.getElementById("cards-container"); cardsContainer.innerHTML = "Loading cards...
"; try { const response = await qortalRequest({ action: "SEARCH_QDN_RESOURCES", service: "BLOG_POST", query: cardIdentifierPrefix, mode: "ALL" }); if (!response || !Array.isArray(response) || response.length === 0) { cardsContainer.innerHTML = "No cards found.
"; return; } // Validate cards and filter const validatedCards = await Promise.all( response.map(async card => { const isValid = await validateCardStructure(card); return isValid ? card : null; }) ); const validCards = validatedCards.filter(card => card !== null); if (validCards.length === 0) { cardsContainer.innerHTML = "No valid cards found.
"; return; } const finalCards = await processMinterCards(validCards) // Sort cards by timestamp descending (newest first) // validCards.sort((a, b) => { // const timestampA = a.updated || a.created || 0; // const timestampB = b.updated || b.created || 0; // return timestampB - timestampA; // }); // Display skeleton cards immediately cardsContainer.innerHTML = ""; finalCards.forEach(card => { const skeletonHTML = createSkeletonCardHTML(card.identifier); cardsContainer.insertAdjacentHTML("beforeend", skeletonHTML); }); // Fetch and update each card finalCards.forEach(async card => { try { const cardDataResponse = await qortalRequest({ action: "FETCH_QDN_RESOURCE", name: card.name, service: "BLOG_POST", identifier: card.identifier, }); if (!cardDataResponse) { console.warn(`Skipping invalid card: ${JSON.stringify(card)}`); removeSkeleton(card.identifier); return; } // Skip cards without polls if (!cardDataResponse.poll) { console.warn(`Skipping card with no poll: ${card.identifier}`); removeSkeleton(card.identifier); return; } // Fetch poll results const pollResults = await fetchPollResults(cardDataResponse.poll); const BgColor = generateDarkPastelBackgroundBy(card.name) // Generate final card HTML const commentCount = await countComments(card.identifier) const cardUpdatedTime = card.updated || null const finalCardHTML = await createCardHTML(cardDataResponse, pollResults, card.identifier, commentCount, cardUpdatedTime, BgColor); replaceSkeleton(card.identifier, finalCardHTML); } catch (error) { console.error(`Error processing card ${card.identifier}:`, error); removeSkeleton(card.identifier); // Silently remove skeleton on error } }); } catch (error) { console.error("Error loading cards:", error); cardsContainer.innerHTML = "Failed to load cards.
"; } }; const removeSkeleton = (cardIdentifier) => { const skeletonCard = document.getElementById(`skeleton-${cardIdentifier}`); if (skeletonCard) { skeletonCard.remove(); // Remove the skeleton silently } }; const replaceSkeleton = (cardIdentifier, htmlContent) => { const skeletonCard = document.getElementById(`skeleton-${cardIdentifier}`); if (skeletonCard) { skeletonCard.outerHTML = htmlContent; } }; // Function to create a skeleton card const createSkeletonCardHTML = (cardIdentifier) => { return `LOADING CARD...
PLEASE BE PATIENT
While data loads from QDN...
${header}
(click COMMENTS button to open/close card comments)
By: ${creator} - ${formattedDate}
${commentDataResponse.creator}:
${commentDataResponse.content}
${timestamp}