Fixes for name-based AdminBoard cards publish issue, minor additional fixes.

This commit is contained in:
crowetic 2024-12-31 19:48:34 -08:00
parent 5a6baaef66
commit 6374ea1e41
4 changed files with 173 additions and 153 deletions

View File

@ -370,12 +370,12 @@
.attachment button {
align-self: flex-end;
margin-top: 1vh;
background-color: #0f4c41;
margin-top: 0.8vh;
background-color: #0f364c;
color: #ffffff;
border: dotted;
border-style: ridge;
border-radius: 1vh;
padding: 0.3vh 0.6vh;
padding: 0.15vh 0.3vh;
cursor: pointer;
}

View File

@ -1,7 +1,7 @@
// 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 = false
const encryptedCardIdentifierPrefix = "card-MAC"
let isExistingEncryptedCard = false
let isUpdateCard = false
let existingDecryptedCardData = {}
let existingEncryptedCardIdentifier = {}
let cardMinterName = {}
@ -19,7 +19,7 @@ const loadAdminBoardPage = async () => {
for (let i = bodyChildren.length - 1; i >= 0; i--) {
const child = bodyChildren[i];
if (!child.classList.contains("menu")) {
child.remove();
child.remove()
}
}
@ -35,17 +35,17 @@ const loadAdminBoardPage = async () => {
<div id="encrypted-cards-container" class="cards-container" style="margin-top: 20px;"></div>
<div id="publish-card-view" class="publish-card-view" style="display: none; text-align: left; padding: 20px;">
<form id="publish-card-form">
<h3>Create or Update Your Minter Card</h3>
<h3>Create or Update an Admin Card</h3>
<div class="publish-card-checkbox" style="margin-top: 1em;">
<input type="checkbox" id="topic-checkbox" name="topicMode" />
<label for="topic-checkbox">Is this a Topic instead of a Minter?</label>
</div>
<label for="minter-name-input">Input Topic or Minter Name:</label>
<input type="text" id="minter-name-input" maxlength="100" placeholder="Enter Topic or Minter's Name" required>
<label for="minter-name-input">Input TOPIC or NAME:</label>
<input type="text" id="minter-name-input" maxlength="100" placeholder="input NAME or TOPIC" required>
<label for="card-header">Header:</label>
<input type="text" id="card-header" maxlength="100" placeholder="Explain main point/issue" required>
<label for="card-content">Content:</label>
<textarea id="card-content" placeholder="Enter any information you like...You may also attach links to more in-depth information, etc. (Links will pop-up in a custom in-app viewer upon being clicked.)" required></textarea>
<textarea id="card-content" placeholder="Enter any information you like... CHECK THE TOPIC CHECKBOX if you do not want to publish a NAME card. NAME cards are verified and can only be one per name. Links are displayed in in-app pop-up." required></textarea>
<label for="card-links">Links (qortal://...):</label>
<div id="links-container">
<input type="text" class="card-link" placeholder="Enter QDN link">
@ -71,7 +71,7 @@ const loadAdminBoardPage = async () => {
refreshCardsButton.addEventListener("click", async () => {
const encryptedCardsContainer = document.getElementById("encrypted-cards-container")
encryptedCardsContainer.innerHTML = "<p>Refreshing cards...</p>"
await fetchAllEncryptedCards()
await fetchAllEncryptedCards(true)
})
}
@ -202,7 +202,7 @@ const processCards = async (validEncryptedCards) => {
//Main function to load the Minter Cards ----------------------------------------
const fetchAllEncryptedCards = async () => {
const fetchAllEncryptedCards = async (isRefresh=false) => {
const encryptedCardsContainer = document.getElementById("encrypted-cards-container")
encryptedCardsContainer.innerHTML = "<p>Loading cards...</p>"
@ -243,8 +243,8 @@ const fetchAllEncryptedCards = async () => {
// Fetch and update each card
finalCards.forEach(async card => {
try {
const hasMinterName = await extractEncryptedCardsMinterName(card.identifier)
if (hasMinterName) existingCardMinterNames.push(hasMinterName)
// const hasMinterName = await extractEncryptedCardsMinterName(card.identifier)
// if (hasMinterName) existingCardMinterNames.push(hasMinterName)
const cardDataResponse = await qortalRequest({
action: "FETCH_QDN_RESOURCE",
@ -274,23 +274,48 @@ const fetchAllEncryptedCards = async () => {
if (encryptedCardPollPublisherPublicKey != encryptedCardPublisherPublicKey) {
console.warn(`QuickMythril cardPollHijack attack found! Not including card with identifier: ${card.identifier}`)
return
}
// Fetch poll results
const pollResults = await fetchPollResults(decryptedCardData.poll)
if (pollResults?.error) {
console.warn(`Skipping card with non-existent poll: ${card.identifier}, poll=${decryptedCardData.poll}`)
removeSkeleton(card.identifier)
return
}
// Fetch poll results and discard cards with no results
const pollResults = await fetchPollResults(decryptedCardData.poll)
if (pollResults?.error) {
console.warn(`Skipping card with failed poll results?: ${card.identifier}, poll=${decryptedCardData.poll}`)
removeSkeleton(card.identifier)
return
}
if (!isRefresh) {
console.log(`This is a REFRESH, NOT adding names to duplicates list...`)
const obtainedMinterName = decryptedCardData.minterName
// if ((obtainedMinterName) && existingCardMinterNames.includes(obtainedMinterName)) {
// console.warn(`name found in existing names array...${obtainedMinterName} skipping duplicate card...${card.identifier}`)
// removeSkeleton(card.identifier)
// return
// } else if ((obtainedMinterName) && (!existingCardMinterNames.includes(obtainedMinterName))) {
// existingCardMinterNames.push(obtainedMinterName)
// console.log(`minterName: ${obtainedMinterName} found, doesn't exist in existing array, added to existingCardMinterNames array`)
// }
if (obtainedMinterName && existingCardMinterNames.some(item => item.minterName === obtainedMinterName)) {
console.warn(`name found in existing names array...${obtainedMinterName} skipping duplicate card...${card.identifier}`)
removeSkeleton(card.identifier)
return
} else if (obtainedMinterName) {
existingCardMinterNames.push({ minterName: obtainedMinterName, identifier: card.identifier })
console.log(`Added minterName and identifier to existingCardMinterNames array:`, { minterName: obtainedMinterName, identifier: card.identifier })
}
}
// const minterNameFromIdentifier = await extractCardsMinterName(card.identifier);
const encryptedCommentCount = await getEncryptedCommentCount(card.identifier)
// Generate final card HTML
const finalCardHTML = await createEncryptedCardHTML(decryptedCardData, pollResults, card.identifier, encryptedCommentCount)
replaceEncryptedSkeleton(card.identifier, finalCardHTML)
replaceSkeleton(card.identifier, finalCardHTML)
} catch (error) {
console.error(`Error processing card ${card.identifier}:`, error)
removeSkeleton(card.identifier)
@ -303,20 +328,20 @@ const fetchAllEncryptedCards = async () => {
}
}
//TODO verify that this actually isn't necessary. if not, remove it.
// const removeEncryptedSkeleton = (cardIdentifier) => {
// const encryptedSkeletonCard = document.getElementById(`skeleton-${cardIdentifier}`)
// if (encryptedSkeletonCard) {
// encryptedSkeletonCard.remove(); // Remove the skeleton silently
// }
// }
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;
}
}
// const replaceEncryptedSkeleton = (cardIdentifier, htmlContent) => {
// const encryptedSkeletonCard = document.getElementById(`skeleton-${cardIdentifier}`)
// if (encryptedSkeletonCard) {
// encryptedSkeletonCard.outerHTML = htmlContent;
// }
// }
// Function to create a skeleton card
const createEncryptedSkeletonCardHTML = (cardIdentifier) => {
@ -338,52 +363,22 @@ const createEncryptedSkeletonCardHTML = (cardIdentifier) => {
// Function to check and fech an existing Minter Card if attempting to publish twice ----------------------------------------
const fetchExistingEncryptedCard = async (minterName) => {
try {
const response = await searchSimple('MAIL_PRIVATE', `${encryptedCardIdentifierPrefix}`, minterName, 0)
const fetchExistingEncryptedCard = async (minterName, existingIdentifier) => {
console.log(`SEARCH_QDN_RESOURCES response: ${JSON.stringify(response, null, 2)}`)
// Step 2: Check if the response is an array and not empty
if (!response || !Array.isArray(response) || response.length === 0) {
console.log("No cards found for the current user.")
return null
}
// Step 3: Validate cards asynchronously
const validatedCards = await Promise.all(
response.map(async card => {
const isValid = await validateEncryptedCardIdentifier(card)
return isValid ? card : null
})
)
// Step 4: Filter out invalid cards
const validCards = validatedCards.filter(card => card !== null)
if (validCards.length > 0) {
// Step 5: Sort by most recent timestamp
const mostRecentCard = validCards.sort((a, b) => b.created - a.created)[0]
// Step 6: Fetch full card data
try{
const cardDataResponse = await qortalRequest({
action: "FETCH_QDN_RESOURCE",
name: mostRecentCard.name,
service: mostRecentCard.service,
identifier: mostRecentCard.identifier,
name: minterName,
service: "MAIL_PRIVATE",
identifier: existingIdentifier,
encoding: "base64"
})
existingEncryptedCardIdentifier = mostRecentCard.identifier
const decryptedCardData = await decryptAndParseObject(cardDataResponse)
console.log("Full card data fetched successfully:", decryptedCardData)
existingDecryptedCardData = await decryptAndParseObject(cardDataResponse)
console.log("Full card data fetched successfully:", existingDecryptedCardData)
return decryptedCardData
return existingDecryptedCardData
}
console.log("No valid cards found.")
return null
} catch (error) {
console.error("Error fetching existing card:", error);
return null
@ -402,16 +397,16 @@ const validateEncryptedCardIdentifier = async (card) => {
}
// Load existing card data passed, into the form for editing -------------------------------------
const loadEncryptedCardIntoForm = async () => {
if (existingDecryptedCardData) {
console.log("Loading existing card data:", existingDecryptedCardData);
document.getElementById("minter-name-input").value = existingDecryptedCardData.minterName
document.getElementById("card-header").value = existingDecryptedCardData.header
document.getElementById("card-content").value = existingDecryptedCardData.content
const loadEncryptedCardIntoForm = async (decryptedCardData) => {
if (decryptedCardData) {
console.log("Loading existing card data:", decryptedCardData);
document.getElementById("minter-name-input").value = decryptedCardData.minterName
document.getElementById("card-header").value = decryptedCardData.header
document.getElementById("card-content").value = decryptedCardData.content
const linksContainer = document.getElementById("links-container")
linksContainer.innerHTML = ""; // Clear previous links
existingDecryptedCardData.links.forEach(link => {
decryptedCardData.links.forEach(link => {
const linkInput = document.createElement("input")
linkInput.type = "text"
linkInput.className = "card-link"
@ -454,19 +449,18 @@ const publishEncryptedCard = async (isTopicModePassed = false) => {
if (!isTopic) {
publishedMinterName = await validateMinterName(minterNameInput)
if (!publishedMinterName) {
alert(`"${minterNameInput}" doesn't seem to be a valid Minter name. Please check or use topic mode.`)
alert(`"${minterNameInput}" doesn't seem to be a valid name. Please check or use topic mode.`)
return
}
// Also check for existing card if not topic
if (!isExistingEncryptedCard && existingCardMinterNames.includes(publishedMinterName)) {
if (!isUpdateCard && existingCardMinterNames.some(item => item.minterName === publishedMinterName)) {
const duplicateCardData = existingCardMinterNames.find(item => item.minterName === publishedMinterName)
const updateCard = confirm(
`Minter Name: ${publishedMinterName} already has a card. Update or Cancel?`
`Minter Name: ${publishedMinterName} already has a card. Duplicate name-based cards are not allowed. You can OVERWRITE it or Cancel publishing. UPDATE CARD?`
)
if (updateCard) {
await fetchExistingEncryptedCard(publishedMinterName)
await loadEncryptedCardIntoForm()
isExistingEncryptedCard = true
return
existingEncryptedCardIdentifier = duplicateCardData.identifier
isUpdateCard = true
} else {
return
}
@ -476,9 +470,9 @@ const publishEncryptedCard = async (isTopicModePassed = false) => {
// Determine final card identifier
const newCardIdentifier = isTopic
? `${encryptedCardIdentifierPrefix}-TOPIC-${await uid()}`
: `${encryptedCardIdentifierPrefix}-${publishedMinterName}-${await uid()}`
: `${encryptedCardIdentifierPrefix}-NC-${Date.now}-${await uid()}`
const cardIdentifier = isExistingEncryptedCard ? existingEncryptedCardIdentifier : newCardIdentifier
const cardIdentifier = isUpdateCard ? existingEncryptedCardIdentifier : newCardIdentifier
// Build cardData
const pollName = `${cardIdentifier}-poll`
@ -527,7 +521,7 @@ const publishEncryptedCard = async (isTopicModePassed = false) => {
})
// Possibly create a poll if its a brand new card
if (!isExistingEncryptedCard) {
if (!isUpdateCard) {
await qortalRequest({
action: "CREATE_POLL",
pollName,
@ -537,13 +531,6 @@ const publishEncryptedCard = async (isTopicModePassed = false) => {
})
alert("Card and poll published successfully!")
// If its a real Minter name, store it so we know we have a card for them
if (!isTopic) {
if (!existingCardMinterNames.contains(publishedMinterName)) {
existingCardMinterNames.push(publishedMinterName)
}
}
} else {
alert("Card updated successfully! (No poll updates possible currently...)");
}
@ -749,21 +736,6 @@ const getMinterAvatar = async (minterName) => {
}
}
// const togglePollDetails = (cardIdentifier) => {
// const detailsDiv = document.getElementById(`poll-details-${cardIdentifier}`)
// const modal = document.getElementById(`poll-details-modal`)
// const modalContent = document.getElementById(`poll-details-modalContent`)
// if (!detailsDiv || !modal || !modalContent) return
// modalContent.appendChild(detailsDiv)
// modal.style.display = 'block'
// window.onclick = (event) => {
// if (event.target === modal) {
// modal.style.display = 'none'
// }
// }
// }
// Create the overall Minter Card HTML -----------------------------------------------
const createEncryptedCardHTML = async (cardData, pollResults, cardIdentifier, commentCount) => {

View File

@ -211,7 +211,7 @@ const loadForumPage = async () => {
const isAdmin = userState.isAdmin;
// 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');
const mainContent = document.createElement('div')
mainContent.innerHTML = `
<div class="forum-main mbr-parallax-background cid-ttRnlSkg2R">
<div class="forum-header" style="color: lightblue; display: flex; justify-content: center; align-items: center; padding: 10px;">
@ -236,18 +236,18 @@ const loadForumPage = async () => {
// Add event listeners to room buttons
document.getElementById("minters-room").addEventListener("click", () => {
currentPage = 0;
loadRoomContent("minters");
});
loadRoomContent("minters")
})
if (userState.isAdmin) {
document.getElementById("admins-room").addEventListener("click", () => {
currentPage = 0;
loadRoomContent("admins");
});
loadRoomContent("admins")
})
}
document.getElementById("general-room").addEventListener("click", () => {
currentPage = 0;
loadRoomContent("general");
});
loadRoomContent("general")
})
}
// Function to add the pagination buttons and related control mechanisms ------------------------
@ -363,25 +363,56 @@ const loadRoomContent = async (room) => {
;
};
// Initialize Quill editor
// Initialize Quill editor //TODO check the updated editor init code
// const initializeQuillEditor = () => {
// new Quill('#editor', {
// theme: 'snow',
// modules: {
// toolbar: [
// [{ 'font': [] }],
// [{ 'size': ['small', false, 'large', 'huge'] }],
// [{ 'header': [1, 2, false] }],
// ['bold', 'italic', 'underline'],
// [{ 'list': 'ordered'}, { 'list': 'bullet' }],
// ['link', 'blockquote', 'code-block'],
// [{ 'color': [] }, { 'background': [] }],
// [{ 'align': [] }],
// ['clean']
// ]
// }
// });
// };
const initializeQuillEditor = () => {
new Quill('#editor', {
const editorContainer = document.querySelector('#editor');
if (!editorContainer) {
console.error("Editor container not found!");
return;
}
new Quill('#editor', {
theme: 'snow',
modules: {
toolbar: [
[{ 'font': [] }],
[{ 'size': ['small', false, 'large', 'huge'] }],
[{ 'header': [1, 2, false] }],
['bold', 'italic', 'underline'],
[{ indent: '-1' }, { indent: '+1' }],
[{ 'header': [1, 2, 3, 5, false] }],
['bold', 'italic', 'underline', 'strike'],
['blockquote', 'code-block'],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
['link', 'blockquote', 'code-block'],
[{ 'color': [] }, { 'background': [] }],
// ['link', 'image', 'video'], //todo attempt to add fancy base64 embed function for images, gif, and maybe small videos.
[{ 'align': [] }],
['clean']
]
}
});
};
}
// Set up modal behavior
const setupModalHandlers = () => {
@ -469,7 +500,7 @@ const setupFileInputs = (room) => {
})
sendButton.addEventListener('click', async () => {
const quill = new Quill('#editor')
const quill = new Quill('#editor') //TODO figure out what is going on with the quill initialization and so forth.
const messageHtml = quill.root.innerHTML.trim()
if (messageHtml || selectedFiles.length > 0 || selectedImages.length > 0) {
@ -591,27 +622,27 @@ const handleSendMessage = async (room, messageHtml, selectedFiles, selectedImage
function clearInputs() {
const quill = new Quill('#editor');
// Clear the file input elements and preview container
document.getElementById('file-input').value = ''
document.getElementById('image-input').value = ''
document.getElementById('preview-container').innerHTML = ''
// Properly reset Quill editor to ensure formatting options don't linger across messages
quill.setContents([]);
quill.setSelection(0,0);
// Reset the Quill editor
const quill = new Quill('editor')
quill.setContents([])
quill.setSelection(0)
// clear the local file input arrays
document.getElementById('file-input').value = "";
document.getElementById('image-input').value = "";
document.getElementById('preview-container').innerHTML = "";
// Reset other state variables
replyToMessageIdentifier = null
multiResource = []
attachmentIdentifiers = []
selectedImages = []
selectedFiles = []
replyToMessageIdentifier = null;
multiResource = [];
attachmentIdentifiers = [];
selectedImages = [];
selectedFiles = [];
// Remove the reply containers
const replyContainer = document.querySelector(".reply-container");
// Remove the reply container
const replyContainer = document.querySelector('.reply-container')
if (replyContainer) {
replyContainer.remove();
replyContainer.remove()
}
}

View File

@ -68,7 +68,7 @@
<img src="assets/images/again-edited-qortal-minting-icon-156x156.png" alt="">
</a>
</span>
<span class="navbar-caption-wrap"><a class="navbar-caption text-primary display-4" href="index.html">Q-Mintership Alpha v0.66b<br></a></span>
<span class="navbar-caption-wrap"><a class="navbar-caption text-primary display-4" href="index.html">Q-Mintership Alpha v0.67b<br></a></span>
</div>
<ul class="navbar-nav nav-dropdown" data-app-modern-menu="true"><li class="nav-item"><a class="nav-link link text-primary display-7" href="MINTERSHIP-FORUM"></a></li></ul>
@ -197,6 +197,23 @@
<section data-bs-version="5.1" class="content7 boldm5 cid-uufIRKtXOO" id="content7-6">
<div class="container">
<div class="row">
<div class="col-12 col-lg-7 card">
<div class="title-wrapper">
<h2 class="mbr-section-title mbr-fonts-style display-2">
v0.67beta 12-31-2024</h2>
</div>
</div>
<div class="col-12 col-lg-5 card">
<div class="text-wrapper">
<p class="mbr-text mbr-fonts-style display-7">
Fixes for name-based cards on the admin board mostly.</p>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-12 col-lg-7 card">
@ -400,7 +417,7 @@
</div>
<a class="link-wrap" href="#">
<p class="mbr-link mbr-fonts-style display-4">Q-Mintership v0.66beta</p>
<p class="mbr-link mbr-fonts-style display-4">Q-Mintership v0.67beta</p>
</a>
</div>
<div class="col-12 col-lg-6">