// NOTE - Change isTestMode to false prior to actual release ---- !important - You may also change identifier if you want to not show older cards. const isEncryptedTestMode = true; const encryptedCardIdentifierPrefix = "test-MDC"; let isExistingEncryptedCard = false; let existingDecryptedCardData = {}; let existingEncryptedCardIdentifier = {}; let cardMinterName = {} let existingCardMinterNames = [] console.log("Attempting to load AdminBoard.js"); const loadAdminBoardPage = 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"); mainContent.innerHTML = `
The Admin Board is an encrypted card publishing board to keep track of minter data for the Minter Admins. Any Admin may publish a card, and related data, make comments on existing cards, and vote on existing card data in support or not of the name on the card. It is essentially a 'project management' tool to assist the Minter Admins in keeping track of the data related to minters they are adding/removing from the minter group.
More functionality will be added over time. One of the first features will be the ability to output the existing card data 'decisions', to a json formatted list in order to allow crowetic to run his script easily until the final Mintership proposal changes are completed, and the MINTER group is transferred to 'null'.
Refreshing cards...
" await fetchAllEncryptedCards() }) } const cancelPublishButton = document.getElementById("cancel-publish-button") if (cancelPublishButton) { cancelPublishButton.addEventListener("click", async () => { const encryptedCardsContainer = document.getElementById("encrypted-cards-container") encryptedCardsContainer.style.display = "flex"; // Restore visibility const publishCardView = document.getElementById("publish-card-view") publishCardView.style.display = "none"; // Hide the publish form }) } const addLinkButton = document.getElementById("add-link-button") if (addLinkButton) { addLinkButton.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 publishEncryptedCard(); }); await createCardMinterNameList(); await fetchAllEncryptedCards(); } const extractCardsMinterName = (cardIdentifier) => { // Ensure the identifier starts with the prefix if (!cardIdentifier.startsWith(`${encryptedCardIdentifierPrefix}-`)) { 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'); } // Extract minterName (everything from the second part to the second-to-last part) const minterName = parts.slice(2, -1).join('-'); // Return the extracted minterName return minterName; } const createCardMinterNameList = async () => { const response = await qortalRequest({ action: "SEARCH_QDN_RESOURCES", service: "MAIL_PRIVATE", query: `${encryptedCardIdentifierPrefix}`, mode: "ALL", }); const validatedEncryptedCards = await Promise.all( response.map(async card => { const isValid = await validateEncryptedCardIdentifier(card); return isValid ? card : null; }) ) const validEncryptedCards = validatedEncryptedCards.filter(card => card !== null); if (validEncryptedCards.length === 0) { console.log(`no matches found, not adding any names to name list.`) return; } for (const result of validEncryptedCards) { const minterName = await extractCardsMinterName(result.identifier) if (!existingCardMinterNames.includes(minterName)) { existingCardMinterNames.push(minterName) console.log(`cardsMinterName: ${minterName} - added to list`) } } }; //Main function to load the Minter Cards ---------------------------------------- const fetchAllEncryptedCards = async () => { const encryptedCardsContainer = document.getElementById("encrypted-cards-container"); encryptedCardsContainer.innerHTML = "Loading cards...
"; try { const response = await qortalRequest({ action: "SEARCH_QDN_RESOURCES", service: "MAIL_PRIVATE", query: encryptedCardIdentifierPrefix, mode: "ALL" }); if (!response || !Array.isArray(response) || response.length === 0) { encryptedCardsContainer.innerHTML = "No cards found.
"; return; } // Validate cards and filter const validatedEncryptedCards = await Promise.all( response.map(async card => { const isValid = await validateEncryptedCardIdentifier(card); return isValid ? card : null; }) ); const validEncryptedCards = validatedEncryptedCards.filter(card => card !== null); if (validEncryptedCards.length === 0) { encryptedCardsContainer.innerHTML = "No valid cards found.
"; return; } // Group by identifier and keep only the newest card for each identifier const latestCardsMap = new Map(); validEncryptedCards.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); } }); // Extract unique cards and sort by timestamp descending const uniqueValidCards = Array.from(latestCardsMap.values()).sort((a, b) => { const timestampA = a.updated || a.created || 0; const timestampB = b.updated || b.created || 0; return timestampB - timestampA; }); // Display skeleton cards immediately encryptedCardsContainer.innerHTML = ""; uniqueValidCards.forEach(card => { const skeletonHTML = createSkeletonCardHTML(card.identifier); encryptedCardsContainer.insertAdjacentHTML("beforeend", skeletonHTML); }); // Fetch and update each card uniqueValidCards.forEach(async card => { try { const cardDataResponse = await qortalRequest({ action: "FETCH_QDN_RESOURCE", name: card.name, service: "MAIL_PRIVATE", identifier: card.identifier, encoding: "base64" }); if (!cardDataResponse) { console.warn(`Skipping invalid card: ${JSON.stringify(card)}`); removeSkeleton(card.identifier); return; } const decryptedCardData = await decryptAndParseObject(cardDataResponse); // Skip cards without polls if (!decryptedCardData.poll) { console.warn(`Skipping card with no poll: ${card.identifier}`); removeSkeleton(card.identifier); return; } // Fetch poll results const pollResults = await fetchPollResults(decryptedCardData.poll); const minterNameFromIdentifier = await extractCardsMinterName(card.identifier); // Generate final card HTML const finalCardHTML = await createEncryptedCardHTML(decryptedCardData, pollResults, card.identifier); replaceEncryptedSkeleton(card.identifier, finalCardHTML); } catch (error) { console.error(`Error processing card ${card.identifier}:`, error); removeEncryptedSkeleton(card.identifier); // Silently remove skeleton on error } }); } catch (error) { console.error("Error loading cards:", error); encryptedCardsContainer.innerHTML = "Failed to load cards.
"; } }; const removeEncryptedSkeleton = (cardIdentifier) => { const encryptedSkeletonCard = document.getElementById(`skeleton-${cardIdentifier}`); if (encryptedSkeletonCard) { encryptedSkeletonCard.remove(); // Remove the skeleton silently } }; const replaceEncryptedSkeleton = (cardIdentifier, htmlContent) => { const encryptedSkeletonCard = document.getElementById(`skeleton-${cardIdentifier}`); if (encryptedSkeletonCard) { encryptedSkeletonCard.outerHTML = htmlContent; } }; // Function to create a skeleton card const createEncryptedSkeletonCardHTML = (cardIdentifier) => { return `${header}
Published by: ${creator} on ${formattedDate}
${decryptedCommentData.creator}:
${decryptedCommentData.content}
${timestamp}