testing-20250128 #7

Closed
Ghost wants to merge 13 commits from (deleted):testing-20250128 into main
5 changed files with 264 additions and 25 deletions

View File

@ -28,7 +28,6 @@ const loadAddRemoveAdminPage = async () => {
</p> </p>
<div id="admin-table-section" class="admin-table-section" style="margin-top: 2em;"> <div id="admin-table-section" class="admin-table-section" style="margin-top: 2em;">
<h3 style="color:rgb(212, 212, 212);">Existing Minter Admins</h3>
<div id="admin-list-container" style="margin: 1em auto; max-width: 600px;"></div> <div id="admin-list-container" style="margin: 1em auto; max-width: 600px;"></div>
</div> </div>
@ -59,6 +58,13 @@ const loadAddRemoveAdminPage = async () => {
<div id="existing-proposals-section" class="proposals-section" style="margin-top: 3em; display: flex; flex-direction: column; justify-content: center; align-items: center;"> <div id="existing-proposals-section" class="proposals-section" style="margin-top: 3em; display: flex; flex-direction: column; justify-content: center; align-items: center;">
<h3 style="color: #ddd;">Existing Promotion/Demotion Proposals</h3> <h3 style="color: #ddd;">Existing Promotion/Demotion Proposals</h3>
<button id="refresh-cards-button" class="refresh-cards-button" style="padding: 10px;">Refresh Proposal Cards</button> <button id="refresh-cards-button" class="refresh-cards-button" style="padding: 10px;">Refresh Proposal Cards</button>
<select id="sort-select" style="margin-left: 10px; padding: 5px;">
<option value="newest" selected>Sort by Date</option>
<option value="name">Sort by Name</option>
<option value="recent-comments">Newest Comments</option>
<option value="least-votes">Least Votes</option>
<option value="most-votes">Most Votes</option>
</select>
<select id="time-range-select" style="margin-left: 10px; padding: 5px;"> <select id="time-range-select" style="margin-left: 10px; padding: 5px;">
<option value="0">Show All</option> <option value="0">Show All</option>
<option value="1">Last 1 day</option> <option value="1">Last 1 day</option>
@ -98,6 +104,14 @@ const loadAddRemoveAdminPage = async () => {
await loadCards(addRemoveIdentifierPrefix) await loadCards(addRemoveIdentifierPrefix)
}) })
document.getElementById("sort-select").addEventListener("change", async () => {
// Optionally clear or show a message while loading
const cardsContainer = document.getElementById("cards-container")
cardsContainer.innerHTML = "<p>Refreshing cards...</p>"
// Re-load the cards using the same function that handles sorting logic
await loadCards(addRemoveIdentifierPrefix)
})
document.getElementById("cancel-publish-button").addEventListener("click", async () => { document.getElementById("cancel-publish-button").addEventListener("click", async () => {
// const cardsContainer = document.getElementById("existing-proposals-section") // const cardsContainer = document.getElementById("existing-proposals-section")
// cardsContainer.style.display = "flex" // Restore visibility // cardsContainer.style.display = "flex" // Restore visibility
@ -133,6 +147,19 @@ const toggleProposeButton = () => {
proposeButton.style.display === 'flex' ? 'none' : 'flex' proposeButton.style.display === 'flex' ? 'none' : 'flex'
} }
const toggleAdminTable = () => {
const tableContainer = document.getElementById("adminTableContainer")
const toggleBtn = document.getElementById("toggleAdminTableButton")
if (tableContainer.style.display === "none") {
tableContainer.style.display = "block"
toggleBtn.textContent = "Hide Minter Admins"
} else {
tableContainer.style.display = "none"
toggleBtn.textContent = "Show Minter Admins"
}
}
const fetchAllARTxData = async () => { const fetchAllARTxData = async () => {
const addAdmTx = "ADD_GROUP_ADMIN" const addAdmTx = "ADD_GROUP_ADMIN"
const remAdmTx = "REMOVE_GROUP_ADMIN" const remAdmTx = "REMOVE_GROUP_ADMIN"
@ -216,6 +243,9 @@ const displayExistingMinterAdmins = async () => {
// 1) Fetch addresses // 1) Fetch addresses
const admins = await fetchMinterGroupAdmins() const admins = await fetchMinterGroupAdmins()
minterAdminAddresses = admins.map(m => m.member) minterAdminAddresses = admins.map(m => m.member)
// Compute total admin count and signatures needed (40%, rounded up)
const totalAdmins = admins.length;
const signaturesNeeded = Math.ceil(totalAdmins * 0.40);
let rowsHtml = ""; let rowsHtml = "";
for (const adminAddr of admins) { for (const adminAddr of admins) {
if (adminAddr.member === nullAddress) { if (adminAddr.member === nullAddress) {
@ -262,6 +292,22 @@ const displayExistingMinterAdmins = async () => {
} }
// 3) Build the table // 3) Build the table
const tableHtml = ` const tableHtml = `
<div style="text-align: center; margin-bottom: 1em;">
<button
id="toggleAdminTableButton"
onclick="toggleAdminTable()"
style="
padding: 10px;
background: #444;
color: #fff;
border-radius: 5px;
cursor: pointer;
"
>
Show Minter Admins
</button>
</div>
<div id="adminTableContainer" style="display: none;">
<table style="width: 100%; border-collapse: collapse;"> <table style="width: 100%; border-collapse: collapse;">
<thead> <thead>
<tr style="background:rgb(21, 36, 18); color:rgb(183, 208, 173); font-size: 1.5rem;"> <tr style="background:rgb(21, 36, 18); color:rgb(183, 208, 173); font-size: 1.5rem;">
@ -274,8 +320,13 @@ const displayExistingMinterAdmins = async () => {
${rowsHtml} ${rowsHtml}
</tbody> </tbody>
</table> </table>
<div>
` `
adminListContainer.innerHTML = tableHtml adminListContainer.innerHTML = `
<h3 style="color:rgb(212, 212, 212);">Existing Minter Admins: ${totalAdmins}</h3>
<h4 style="color:rgb(212, 212, 212);">Signatures for Group Approval (40%): ${signaturesNeeded}</h4>
${tableHtml}
`;
} catch (err) { } catch (err) {
console.error("Error fetching minter admins:", err) console.error("Error fetching minter admins:", err)
adminListContainer.innerHTML = adminListContainer.innerHTML =
@ -550,7 +601,7 @@ const checkAndDisplayActions = async (adminYes, name, cardIdentifier) => {
} else if ((minterAdmins) && (minterAdmins.length > 1) && isBlockPassed){ } else if ((minterAdmins) && (minterAdmins.length > 1) && isBlockPassed){
const totalAdmins = minterAdmins.length const totalAdmins = minterAdmins.length
const fortyPercent = totalAdmins * 0.40 const fortyPercent = totalAdmins * 0.40
minAdminCount = Math.round(fortyPercent) minAdminCount = Math.ceil(fortyPercent)
console.warn(`this is another check to ensure minterAdmin group has more than 1 admin. IF so we will calculate the 40% needed for GROUP_APPROVAL, that number is: ${minAdminCount}`) console.warn(`this is another check to ensure minterAdmin group has more than 1 admin. IF so we will calculate the 40% needed for GROUP_APPROVAL, that number is: ${minAdminCount}`)
} }
const addressInfo = await getNameInfo(name) const addressInfo = await getNameInfo(name)
@ -711,6 +762,47 @@ const handleRemoveMinterGroupAdmin = async (name, address) => {
} }
} }
// ADDED: A simple function to effectively 'delete' an AR Board card
// by publishing an empty card with the same identifier and prefix
const deleteARCard = async (cardIdentifier, prefix) => {
try {
const confirmed = confirm("Are you sure you want to delete this card? This action cannot be undone.")
if (!confirmed) return
// A minimal blank object
const blankData = {
header: "",
content: "",
links: [],
creator: userState.accountName,
timestamp: Date.now(),
poll: "" // or null. This ensures it won't appear as a valid poll card
}
let base64Data = await objectToBase64(blankData)
if (!base64Data) {
base64Data = btoa(JSON.stringify(blankData))
}
await qortalRequest({
action: "PUBLISH_QDN_RESOURCE",
name: userState.accountName,
service: "BLOG_POST", // same as all ARBoard content
identifier: cardIdentifier,
data64: base64Data,
})
alert("Your card has been effectively deleted.")
// Now reload the existing ARBoard cards so the UI no longer shows the old card
await loadCards(prefix)
} catch (error) {
console.error("Error deleting AR card:", error)
alert("Failed to delete the card. Check console for details.")
}
}
const fallbackMinterCheck = async (minterName, minterGroupMembers, minterAdmins) => { const fallbackMinterCheck = async (minterName, minterGroupMembers, minterAdmins) => {
// Ensure we have addresses // Ensure we have addresses
if (!minterGroupMembers) { if (!minterGroupMembers) {
@ -909,6 +1001,16 @@ const createARCardHTML = async (cardData, pollResults, cardIdentifier, commentCo
<button class="no" onclick="voteNoOnPoll('${poll}')">NO</button> <button class="no" onclick="voteNoOnPoll('${poll}')">NO</button>
</div> </div>
</div> </div>
${creator === userState.accountName ? `
<div style="margin-top: 0.8em;">
<button
style="padding: 10px; background: darkred; color: white; border-radius: 4px; cursor: pointer;"
onclick="deleteARCard('${cardIdentifier}', '${addRemoveIdentifierPrefix}')"
>
DELETE CARD
</button>
</div>
` : ''}
<div id="comments-section-${cardIdentifier}" class="comments-section" style="display: none; margin-top: 20px;"> <div id="comments-section-${cardIdentifier}" class="comments-section" style="display: none; margin-top: 20px;">
<div id="comments-container-${cardIdentifier}" class="comments-container"></div> <div id="comments-container-${cardIdentifier}" class="comments-container"></div>
<textarea id="new-comment-${cardIdentifier}" placeholder="Input your comment..." style="width: 100%; margin-top: 10px;"></textarea> <textarea id="new-comment-${cardIdentifier}" placeholder="Input your comment..." style="width: 100%; margin-top: 10px;"></textarea>

View File

@ -1067,7 +1067,7 @@ const checkAndDisplayRemoveActions = async (adminYes, name, cardIdentifier, name
} else if ((minterAdmins) && (minterAdmins.length > 1) && isBlockPassed){ } else if ((minterAdmins) && (minterAdmins.length > 1) && isBlockPassed){
const totalAdmins = minterAdmins.length const totalAdmins = minterAdmins.length
const fortyPercent = totalAdmins * 0.40 const fortyPercent = totalAdmins * 0.40
minAdminCount = Math.round(fortyPercent) minAdminCount = Math.ceil(fortyPercent)
console.warn(`this is another check to ensure minterAdmin group has more than 1 admin. IF so we will calculate the 40% needed for GROUP_APPROVAL, that number is: ${minAdminCount}`) console.warn(`this is another check to ensure minterAdmin group has more than 1 admin. IF so we will calculate the 40% needed for GROUP_APPROVAL, that number is: ${minAdminCount}`)
} }
if (isBlockPassed && (userState.isMinterAdmin || userState.isAdmin)) { if (isBlockPassed && (userState.isMinterAdmin || userState.isAdmin)) {
@ -1258,6 +1258,51 @@ const getNewestAdminCommentTimestamp = async (cardIdentifier) => {
} }
} }
// ADDED: A simple function to effectively 'delete' an Admin Board card
// by publishing an empty card with the same identifier and prefix
const deleteAdminCard = async (cardIdentifier) => {
try {
const confirmed = confirm("Are you sure you want to delete this card? This action cannot be undone.")
if (!confirmed) return
// A minimal blank object
const blankData = {
header: "",
content: "",
links: [],
creator: userState.accountName,
timestamp: Date.now(),
poll: "" // or null. This ensures it won't appear as a valid poll card
}
let base64Data = await objectToBase64(blankData)
if (!base64Data) {
base64Data = btoa(JSON.stringify(blankData))
}
const verifiedAdminPublicKeys = await fetchAdminGroupsMembersPublicKeys()
await qortalRequest({
action: "PUBLISH_QDN_RESOURCE",
name: userState.accountName,
service: "MAIL_PRIVATE",
identifier: cardIdentifier,
data64: base64Data,
encrypt: true,
publicKeys: verifiedAdminPublicKeys
})
alert("Your card has been effectively deleted.")
// Now reload the existing Admin Board cards so the UI no longer shows the old card
await fetchAllEncryptedCards(true)
} catch (error) {
console.error("Error deleting Admin card:", error)
alert("Failed to delete the card. Check console for details.")
}
}
// Create the overall Minter Card HTML ----------------------------------------------- // Create the overall Minter Card HTML -----------------------------------------------
const createEncryptedCardHTML = async (cardData, pollResults, cardIdentifier, commentCount) => { const createEncryptedCardHTML = async (cardData, pollResults, cardIdentifier, commentCount) => {
const { minterName, header, content, links, creator, timestamp, poll, topicMode } = cardData const { minterName, header, content, links, creator, timestamp, poll, topicMode } = cardData
@ -1329,9 +1374,9 @@ const createEncryptedCardHTML = async (cardData, pollResults, cardIdentifier, co
const removeActionsHtml = verifiedAddress ? await checkAndDisplayRemoveActions(adminYes, verifiedAddress, cardIdentifier) : await checkAndDisplayRemoveActions(adminYes, verifiedName, cardIdentifier) const removeActionsHtml = verifiedAddress ? await checkAndDisplayRemoveActions(adminYes, verifiedAddress, cardIdentifier) : await checkAndDisplayRemoveActions(adminYes, verifiedName, cardIdentifier)
showRemoveHtml = removeActionsHtml showRemoveHtml = removeActionsHtml
if (userVote === 0) { if (userVote === 0) {
cardColorCode = "rgba(1, 65, 39, 0.41)"; // or any green you want cardColorCode = "rgba(1, 128, 20, 0.35)"; // or any green you want
} else if (userVote === 1) { } else if (userVote === 1) {
cardColorCode = "rgba(55, 12, 12, 0.61)"; // or any red you want cardColorCode = "rgba(124, 6, 6, 0.45)"; // or any red you want
} }
if (banTransactions.some((banTx) => banTx.groupId === 694 && banTx.offender === accountAddress)){ if (banTransactions.some((banTx) => banTx.groupId === 694 && banTx.offender === accountAddress)){
@ -1410,6 +1455,16 @@ const createEncryptedCardHTML = async (cardData, pollResults, cardIdentifier, co
<button class="no" onclick="voteNoOnPoll('${poll}')">NO</button> <button class="no" onclick="voteNoOnPoll('${poll}')">NO</button>
</div> </div>
</div> </div>
${creator === userState.accountName ? `
<div style="margin-top: 0.8em;">
<button
style="padding: 10px; background: darkred; color: white; border-radius: 4px; cursor: pointer;"
onclick="deleteAdminCard('${cardIdentifier}')"
>
DELETE CARD
</button>
</div>
` : ''}
<div id="comments-section-${cardIdentifier}" class="comments-section" style="display: none; margin-top: 20px;"> <div id="comments-section-${cardIdentifier}" class="comments-section" style="display: none; margin-top: 20px;">
<div id="comments-container-${cardIdentifier}" class="comments-container"></div> <div id="comments-container-${cardIdentifier}" class="comments-container"></div>
<textarea id="new-comment-${cardIdentifier}" placeholder="Input your comment..." style="width: 100%; margin-top: 10px;"></textarea> <textarea id="new-comment-${cardIdentifier}" placeholder="Input your comment..." style="width: 100%; margin-top: 10px;"></textarea>

View File

@ -8,7 +8,7 @@ const MIN_ADMIN_YES_VOTES = 9;
const GROUP_APPROVAL_FEATURE_TRIGGER_HEIGHT = 2012800 //TODO update this to correct featureTrigger height when known, either that, or pull from core. const GROUP_APPROVAL_FEATURE_TRIGGER_HEIGHT = 2012800 //TODO update this to correct featureTrigger height when known, either that, or pull from core.
let featureTriggerPassed = false let featureTriggerPassed = false
let isApproved = false let isApproved = false
const spamNames = ["Exorcist"]
const loadMinterBoardPage = async () => { const loadMinterBoardPage = async () => {
// Clear existing content on the page // Clear existing content on the page
@ -378,7 +378,7 @@ const processARBoardCards = async (allValidCards) => {
const loadCards = async (cardIdentifierPrefix) => { const loadCards = async (cardIdentifierPrefix) => {
const cardsContainer = document.getElementById("cards-container") const cardsContainer = document.getElementById("cards-container")
let isARBoard = false let isARBoard = false
cardsContainer.innerHTML = "<p>Loading cards...</p>" cardsContainer.innerHTML = `<p style="color:dodgerblue;">Loading cards...</p>`
if (cardIdentifierPrefix.startsWith("QM-AR-card")) { if (cardIdentifierPrefix.startsWith("QM-AR-card")) {
isARBoard = true isARBoard = true
@ -1118,6 +1118,11 @@ const displayComments = async (cardIdentifier) => {
const commentHTMLArray = await Promise.all( const commentHTMLArray = await Promise.all(
comments.map(async (comment) => { comments.map(async (comment) => {
try { try {
// If the name of the commenter is in the "spamNames" array, return null
if (spamNames.includes(comment.name)) {
console.warn(`Commenter ${comment.name} is in the spamNames array, skipping...`)
return null
}
const commentDataResponse = await qortalRequest({ const commentDataResponse = await qortalRequest({
action: "FETCH_QDN_RESOURCE", action: "FETCH_QDN_RESOURCE",
name: comment.name, name: comment.name,
@ -1394,10 +1399,18 @@ const handleInviteMinter = async (minterName) => {
} }
} }
function escapeForHtmlAttribute(str) {
return str
.replace(/'/g, '&#39;')
.replace(/"/g, '&quot;');
}
const createInviteButtonHtml = (creator, cardIdentifier) => { const createInviteButtonHtml = (creator, cardIdentifier) => {
// Safely escape special chars so they won't break the HTML attribute
const safeCreator = escapeForHtmlAttribute(creator);
return ` return `
<div id="invite-button-container-${cardIdentifier}" style="margin-top: 1em;"> <div id="invite-button-container-${cardIdentifier}" style="margin-top: 1em;">
<button onclick="handleInviteMinter('${creator}')" <button onclick="handleInviteMinter('${safeCreator}')"
style="padding: 10px; background:rgb(0, 109, 76) ; color: white; border: dotted; border-color: white; cursor: pointer; border-radius: 5px;" style="padding: 10px; background:rgb(0, 109, 76) ; color: white; border: dotted; border-color: white; cursor: pointer; border-radius: 5px;"
onmouseover="this.style.backgroundColor='rgb(25, 47, 39) '" onmouseover="this.style.backgroundColor='rgb(25, 47, 39) '"
onmouseout="this.style.backgroundColor='rgba(7, 122, 101, 0.63) '" onmouseout="this.style.backgroundColor='rgba(7, 122, 101, 0.63) '"
@ -1424,17 +1437,12 @@ const featureTriggerCheck = async () => {
const checkAndDisplayInviteButton = async (adminYes, creator, cardIdentifier) => { const checkAndDisplayInviteButton = async (adminYes, creator, cardIdentifier) => {
if (!userState.isMinterAdmin){
console.warn(`User is NOT an admin, not displaying invite/approve button...`)
return null
}
const isBlockPassed = await featureTriggerCheck() const isBlockPassed = await featureTriggerCheck()
const minterAdmins = await fetchMinterGroupAdmins() const minterAdmins = await fetchMinterGroupAdmins()
let minAdminCount = 9 let minAdminCount = 9
if (isBlockPassed) { if (isBlockPassed) {
minAdminCount = Math.round(minterAdmins.length * 0.4) minAdminCount = Math.ceil(minterAdmins.length * 0.4)
console.warn(`Using 40% => ${minAdminCount}`) console.warn(`Using 40% => ${minAdminCount}`)
} }
@ -1475,7 +1483,7 @@ const checkAndDisplayInviteButton = async (adminYes, creator, cardIdentifier) =>
console.warn(`PriorBanOrKick determination:`, priorBanOrKick) console.warn(`PriorBanOrKick determination:`, priorBanOrKick)
const inviteButtonHtml = createInviteButtonHtml(creator, cardIdentifier) const inviteButtonHtml = userState.isMinterAdmin ? createInviteButtonHtml(creator, cardIdentifier) : ''
const groupApprovalHtml = await checkGroupApprovalAndCreateButton(minterAddress, cardIdentifier, "GROUP_INVITE") const groupApprovalHtml = await checkGroupApprovalAndCreateButton(minterAddress, cardIdentifier, "GROUP_INVITE")
if (!priorBanOrKick) { if (!priorBanOrKick) {
@ -1564,7 +1572,8 @@ const checkGroupApprovalAndCreateButton = async (address, cardIdentifier, transa
Existing ${transactionType} Approvals: ${uniqueApprovalCount} Existing ${transactionType} Approvals: ${uniqueApprovalCount}
</p> </p>
${tableHtml} ${tableHtml}
<div id="approval-button-container-${cardIdentifier}" style="margin-top: 1em;"> ${userState.isMinterAdmin ?
`<div id="approval-button-container-${cardIdentifier}" style="margin-top: 1em;">
<button <button
style=" style="
padding: 8px; padding: 8px;
@ -1581,7 +1590,8 @@ const checkGroupApprovalAndCreateButton = async (address, cardIdentifier, transa
> >
Approve Invite Tx Approve Invite Tx
</button> </button>
</div> </div>`
: ''}
</div> </div>
` `
return approvalButtonHtml return approvalButtonHtml
@ -1741,9 +1751,19 @@ async function buildApprovalTableHtml(approvalTxs, getNameFunc) {
// Format the transaction timestamp // Format the transaction timestamp
const dateStr = new Date(tx.timestamp).toLocaleString() const dateStr = new Date(tx.timestamp).toLocaleString()
// Check whether this is the current user
const isCurrentUser =
userState &&
userState.accountName &&
adminName &&
adminName.toLowerCase() === userState.accountName.toLowerCase();
// If it's the current user, highlight the row (change to any color/style you prefer)
const rowStyle = isCurrentUser
? "background: rgba(178, 255, 89, 0.2);" // light green highlight
: "";
return ` return `
<tr> <tr style="${rowStyle}">
<td style="border: 1px solid rgb(255, 255, 255); padding: 4px; color: #234565">${displayName}</td> <td style="border: 1px solid rgb(255, 255, 255); padding: 4px; color: dodgerblue">${displayName}</td>
<td style="border: 1px solid rgb(255, 254, 254); padding: 4px;">${dateStr}</td> <td style="border: 1px solid rgb(255, 254, 254); padding: 4px;">${dateStr}</td>
</tr> </tr>
` `
@ -1885,6 +1905,47 @@ const getNewestCommentTimestamp = async (cardIdentifier) => {
} }
} }
// ADDED: A simple function to effectively 'delete' a Minter Board card
// by publishing an empty card with the same identifier and prefix
const deleteCard = async (cardIdentifier, prefix) => {
try {
const confirmed = confirm("Are you sure you want to delete this card? This action cannot be undone.")
if (!confirmed) return
// A minimal blank object
const blankData = {
header: "",
content: "",
links: [],
creator: userState.accountName,
timestamp: Date.now(),
poll: "" // or null. This ensures it won't appear as a valid poll card
}
let base64Data = await objectToBase64(blankData)
if (!base64Data) {
base64Data = btoa(JSON.stringify(blankData))
}
await qortalRequest({
action: "PUBLISH_QDN_RESOURCE",
name: userState.accountName,
service: "BLOG_POST",
identifier: cardIdentifier,
data64: base64Data,
})
alert("Your card has been effectively deleted.")
// Now reload the existing Minter Board cards so the UI no longer shows the old card
await loadCards(prefix)
} catch (error) {
console.error("Error deleting Minter card:", error)
alert("Failed to delete the card. Check console for details.")
}
}
// Create the overall Minter Card HTML ----------------------------------------------- // Create the overall Minter Card HTML -----------------------------------------------
const createCardHTML = async (cardData, pollResults, cardIdentifier, commentCount, cardUpdatedTime, bgColor, address) => { const createCardHTML = async (cardData, pollResults, cardIdentifier, commentCount, cardUpdatedTime, bgColor, address) => {
const { header, content, links, creator, timestamp, poll } = cardData const { header, content, links, creator, timestamp, poll } = cardData
@ -1992,6 +2053,16 @@ const createCardHTML = async (cardData, pollResults, cardIdentifier, commentCoun
<button class="no" onclick="voteNoOnPoll('${poll}')">NO</button> <button class="no" onclick="voteNoOnPoll('${poll}')">NO</button>
</div> </div>
</div> </div>
${creator === userState.accountName ? `
<div style="margin-top: 0.8em;">
<button
style="padding: 10px; background: darkred; color: white; border-radius: 4px; cursor: pointer;"
onclick="deleteCard('${cardIdentifier}', '${minterCardIdentifierPrefix}')"
>
DELETE CARD
</button>
</div>
` : ''}
<div id="comments-section-${cardIdentifier}" class="comments-section" style="display: none; margin-top: 20px;"> <div id="comments-section-${cardIdentifier}" class="comments-section" style="display: none; margin-top: 20px;">
<div id="comments-container-${cardIdentifier}" class="comments-container"></div> <div id="comments-container-${cardIdentifier}" class="comments-container"></div>
<textarea id="new-comment-${cardIdentifier}" placeholder="Write a comment..." style="width: 100%; margin-top: 10px;"></textarea> <textarea id="new-comment-${cardIdentifier}" placeholder="Write a comment..." style="width: 100%; margin-top: 10px;"></textarea>

View File

@ -331,7 +331,8 @@ const getNameInfo = async (name) => {
console.log('getNameInfo called') console.log('getNameInfo called')
console.log('name:', name) console.log('name:', name)
try { try {
const response = await fetch(`${baseUrl}/names/${name}`) // Encode the name for URL safety
const response = await fetch(`${baseUrl}/names/${encodeURIComponent(name)}`)
if (!response.ok) { if (!response.ok) {
console.warn(`Failed to fetch name info for: ${name}, status: ${response.status}`) console.warn(`Failed to fetch name info for: ${name}, status: ${response.status}`)

View File

@ -30,6 +30,10 @@
<link href="./assets/quill/quill.snow.css" rel="stylesheet"> <link href="./assets/quill/quill.snow.css" rel="stylesheet">
</head> </head>
<body> <body>
<script>
// Define one variable for your version string:
const Q_MINTERSHIP_VERSION = "1.04"; // Update here in the future
</script>
<section data-bs-version="5.1" class="menu menu1 boldm5 cid-ttRnktJ11Q" once="menu" id="menu1-0"> <section data-bs-version="5.1" class="menu menu1 boldm5 cid-ttRnktJ11Q" once="menu" id="menu1-0">
@ -42,7 +46,7 @@
</a> </a>
</span> </span>
<span class="navbar-caption-wrap"> <span class="navbar-caption-wrap">
<a class="navbar-caption display-4" href="index.html">Q-Mintership (v1.04b) <a class="navbar-caption display-4" href="index.html"><span id="versionString1" class="navbar-caption display-4"></span>
</a> </a>
</span> </span>
</div> </div>
@ -61,7 +65,7 @@
<img src="assets/images/again-edited-qortal-minting-icon-156x156.png" alt=""> <img src="assets/images/again-edited-qortal-minting-icon-156x156.png" alt="">
</a> </a>
</span> </span>
<span class="navbar-caption-wrap"><a class="navbar-caption text-primary display-4" href="index.html">Q-Mintership v1.04b<br></a></span> <span class="navbar-caption-wrap"><a class="navbar-caption text-primary display-4" href="index.html"><span id="versionString2" class="navbar-caption text-primary display-4"></span></a></span>
</div> </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> <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>
@ -572,12 +576,12 @@
<div class="title-wrapper"> <div class="title-wrapper">
<div class="title-wrap"> <div class="title-wrap">
<img src="assets/images/again-edited-qortal-minting-icon-156x156.png" alt=""> <img src="assets/images/again-edited-qortal-minting-icon-156x156.png" alt="">
<h2 class="mbr-section-title mbr-fonts-style display-5">Q-Mintership (v1.04b)</h2> <h2 class="mbr-section-title mbr-fonts-style display-5"><span id="versionString3" class="mbr-section-title mbr-fonts-style display-5"></span></h2>
</div> </div>
</div> </div>
<a class="link-wrap" href="#"> <a class="link-wrap" href="#">
<p class="mbr-link mbr-fonts-style display-4">Q-Mintership v1.04beta</p> <p class="mbr-link mbr-fonts-style display-4"><span id="versionString4" class="mbr-link mbr-fonts-style display-4"></span></p>
</a> </a>
</div> </div>
<div class="col-12 col-lg-6"> <div class="col-12 col-lg-6">
@ -590,6 +594,12 @@
</div> </div>
</section> </section>
<script>
document.getElementById("versionString1").textContent = `Q-Mintership (v${Q_MINTERSHIP_VERSION}b)`;
document.getElementById("versionString2").textContent = `Q-Mintership v${Q_MINTERSHIP_VERSION}b`;
document.getElementById("versionString3").textContent = `Q-Mintership (v${Q_MINTERSHIP_VERSION}b)`;
document.getElementById("versionString4").textContent = `Q-Mintership v${Q_MINTERSHIP_VERSION}beta`;
</script>
<script src="./assets/bootstrap/js/bootstrap.bundle.min.js"></script> <script src="./assets/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="./assets/parallax/jarallax.js"></script> <script src="./assets/parallax/jarallax.js"></script>