testing-20250128 #7
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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, ''')
|
||||||
|
.replace(/"/g, '"');
|
||||||
|
}
|
||||||
|
|
||||||
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>
|
||||||
|
@ -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}`)
|
||||||
|
18
index.html
18
index.html
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user