testing-20250123 #6
@ -28,7 +28,6 @@ const loadAddRemoveAdminPage = async () => {
|
||||
</p>
|
||||
|
||||
<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>
|
||||
|
||||
@ -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;">
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
<div id="cards-container" class="cards-container" style="margin-top: 1rem"">
|
||||
<!-- We'll fill this with existing proposal cards -->
|
||||
@ -87,10 +93,18 @@ const loadAddRemoveAdminPage = async () => {
|
||||
|
||||
document.getElementById("refresh-cards-button").addEventListener("click", async () => {
|
||||
const cardsContainer = document.getElementById("cards-container")
|
||||
cardsContainer.innerHTML = "<p>Refreshing cards...</p>"
|
||||
cardsContainer.innerHTML = `<p style="color:dodgerblue;">Refreshing cards...</p>`
|
||||
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 style="color:dodgerblue;">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 () => {
|
||||
// const cardsContainer = document.getElementById("existing-proposals-section")
|
||||
// cardsContainer.style.display = "flex" // Restore visibility
|
||||
@ -126,6 +140,19 @@ const toggleProposeButton = () => {
|
||||
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";
|
||||
}
|
||||
};
|
||||
|
||||
let addAdminTxs
|
||||
let remAdminTxs
|
||||
|
||||
@ -164,9 +191,8 @@ const fetchAllARTxData = async () => {
|
||||
|
||||
// Filter out members with both pending and non-pending transactions
|
||||
return Object.values(adminTxMap)
|
||||
.filter((txs) => !(txs.some(tx => tx.approvalStatus === 'PENDING') &&
|
||||
txs.some(tx => tx.approvalStatus !== 'PENDING')))
|
||||
.flat()
|
||||
.filter(txs => txs.every(tx => tx.approvalStatus !== 'PENDING'))
|
||||
.flat()
|
||||
}
|
||||
|
||||
// Fetch ban transactions
|
||||
@ -209,6 +235,9 @@ const displayExistingMinterAdmins = async () => {
|
||||
// 1) Fetch addresses
|
||||
const admins = await fetchMinterGroupAdmins()
|
||||
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 = "";
|
||||
for (const adminAddr of admins) {
|
||||
if (adminAddr.member === nullAddress) {
|
||||
@ -255,6 +284,22 @@ const displayExistingMinterAdmins = async () => {
|
||||
}
|
||||
// 3) Build the table
|
||||
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;">
|
||||
<thead>
|
||||
<tr style="background:rgb(21, 36, 18); color:rgb(183, 208, 173); font-size: 1.5rem;">
|
||||
@ -267,8 +312,13 @@ const displayExistingMinterAdmins = async () => {
|
||||
${rowsHtml}
|
||||
</tbody>
|
||||
</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) {
|
||||
console.error("Error fetching minter admins:", err)
|
||||
adminListContainer.innerHTML =
|
||||
@ -542,7 +592,7 @@ const checkAndDisplayActions = async (adminYes, name, cardIdentifier) => {
|
||||
} else if ((minterAdmins) && (minterAdmins.length > 1) && isBlockPassed){
|
||||
const totalAdmins = minterAdmins.length
|
||||
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}`)
|
||||
}
|
||||
const addressInfo = await getNameInfo(name)
|
||||
@ -703,6 +753,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) => {
|
||||
// Ensure we have addresses
|
||||
if (!minterGroupMembers) {
|
||||
@ -879,6 +970,16 @@ const createARCardHTML = async (cardData, pollResults, cardIdentifier, commentCo
|
||||
<button class="no" onclick="voteNoOnPoll('${poll}')">NO</button>
|
||||
</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-container-${cardIdentifier}" class="comments-container"></div>
|
||||
<textarea id="new-comment-${cardIdentifier}" placeholder="Input your comment..." style="width: 100%; margin-top: 10px;"></textarea>
|
||||
|
@ -130,7 +130,7 @@ const loadAdminBoardPage = async () => {
|
||||
if (refreshCardsButton) {
|
||||
refreshCardsButton.addEventListener("click", async () => {
|
||||
const encryptedCardsContainer = document.getElementById("encrypted-cards-container")
|
||||
encryptedCardsContainer.innerHTML = "<p>Refreshing cards...</p>"
|
||||
encryptedCardsContainer.innerHTML = `<p style="color:dodgerblue;">Refreshing cards...</p>`
|
||||
await fetchAllEncryptedCards(true)
|
||||
})
|
||||
}
|
||||
@ -325,7 +325,7 @@ const extractEncryptedCardsMinterName = (cardIdentifier) => {
|
||||
|
||||
const fetchAllEncryptedCards = async (isRefresh = false) => {
|
||||
const encryptedCardsContainer = document.getElementById("encrypted-cards-container")
|
||||
encryptedCardsContainer.innerHTML = "<p>Loading cards...</p>"
|
||||
encryptedCardsContainer.innerHTML = `<p style="color:dodgerblue;">Loading cards...</p>`
|
||||
|
||||
try {
|
||||
const response = await searchSimple('MAIL_PRIVATE', `${encryptedCardIdentifierPrefix}`, '', 0)
|
||||
@ -1049,7 +1049,7 @@ const checkAndDisplayRemoveActions = async (adminYes, name, cardIdentifier) => {
|
||||
} else if ((minterAdmins) && (minterAdmins.length > 1) && isBlockPassed){
|
||||
const totalAdmins = minterAdmins.length
|
||||
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}`)
|
||||
}
|
||||
if (isBlockPassed && userState.isMinterAdmin) {
|
||||
@ -1222,6 +1222,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 -----------------------------------------------
|
||||
const createEncryptedCardHTML = async (cardData, pollResults, cardIdentifier, commentCount) => {
|
||||
const { minterName, header, content, links, creator, timestamp, poll, topicMode } = cardData
|
||||
@ -1286,9 +1331,9 @@ const createEncryptedCardHTML = async (cardData, pollResults, cardIdentifier, co
|
||||
const removeActionsHtml = await checkAndDisplayRemoveActions(adminYes, verifiedName, cardIdentifier)
|
||||
showRemoveHtml = removeActionsHtml
|
||||
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) {
|
||||
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)){
|
||||
@ -1367,6 +1412,16 @@ const createEncryptedCardHTML = async (cardData, pollResults, cardIdentifier, co
|
||||
<button class="no" onclick="voteNoOnPoll('${poll}')">NO</button>
|
||||
</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-container-${cardIdentifier}" class="comments-container"></div>
|
||||
<textarea id="new-comment-${cardIdentifier}" placeholder="Input your comment..." style="width: 100%; margin-top: 10px;"></textarea>
|
||||
|
@ -28,10 +28,10 @@ async function loadMinterAdminToolsPage() {
|
||||
<img src="${avatarUrl}" alt="User Avatar" class="user-avatar" style="width: 50px; height: 50px; border-radius: 50%; margin-right: 10px;">
|
||||
<span>${userState.accountName || 'Guest'}</span>
|
||||
</div>
|
||||
<div><h2>No Functionality Here Yet</h2></div>
|
||||
<div><h2>COMING SOON...</h2></div>
|
||||
<div>
|
||||
<p>This page is still under development. Until the final Mintership proposal modifications are made, and the MINTER group is transferred to null, there is no need for this page's functionality. The page will be updated when the final modifications are made.</p>
|
||||
<p> This page until then is simply a placeholder.</p>
|
||||
<p>This page will have functionality to assist the Minter Admins in performing their duties. It will display all pending transactions (of any kind they can approve/deny) along with that ability. It can also be utilized to obtain more in-depth information about existing accounts.</p>
|
||||
<p> The page will be getting a significant overhaul in the near(ish) future, as the MINTER group is now owned by null, and we are past the 'temporary state' we were in for much longer than planned.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -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.
|
||||
let featureTriggerPassed = false
|
||||
let isApproved = false
|
||||
|
||||
const spamNames = ["Exorcist"]
|
||||
|
||||
const loadMinterBoardPage = async () => {
|
||||
// Clear existing content on the page
|
||||
@ -101,7 +101,7 @@ const loadMinterBoardPage = async () => {
|
||||
|
||||
document.getElementById("refresh-cards-button").addEventListener("click", async () => {
|
||||
const cardsContainer = document.getElementById("cards-container")
|
||||
cardsContainer.innerHTML = "<p>Refreshing cards...</p>"
|
||||
cardsContainer.innerHTML = `<p style="color:dodgerblue;">Refreshing cards...</p>`
|
||||
await loadCards(minterCardIdentifierPrefix)
|
||||
})
|
||||
|
||||
@ -265,7 +265,7 @@ const processMinterCards = async (validMinterCards) => {
|
||||
const loadCards = async (cardIdentifierPrefix) => {
|
||||
const cardsContainer = document.getElementById("cards-container")
|
||||
let isARBoard = false
|
||||
cardsContainer.innerHTML = "<p>Loading cards...</p>"
|
||||
cardsContainer.innerHTML = `<p style="color:dodgerblue;">Loading cards...</p>`
|
||||
if ((cardIdentifierPrefix.startsWith(`QM-AR-card`))) {
|
||||
isARBoard = true
|
||||
console.warn(`ARBoard determined:`, isARBoard)
|
||||
@ -1021,6 +1021,11 @@ const displayComments = async (cardIdentifier) => {
|
||||
const commentHTMLArray = await Promise.all(
|
||||
comments.map(async (comment) => {
|
||||
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({
|
||||
action: "FETCH_QDN_RESOURCE",
|
||||
name: comment.name,
|
||||
@ -1297,14 +1302,24 @@ const handleInviteMinter = async (minterName) => {
|
||||
}
|
||||
}
|
||||
|
||||
function escapeForHtmlAttribute(str) {
|
||||
return str
|
||||
.replace(/'/g, ''')
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
const createInviteButtonHtml = (creator, cardIdentifier) => {
|
||||
// Safely escape special chars so they won't break the HTML attribute
|
||||
const safeCreator = escapeForHtmlAttribute(creator);
|
||||
|
||||
return `
|
||||
<div id="invite-button-container-${cardIdentifier}" style="margin-top: 1em;">
|
||||
<button onclick="handleInviteMinter('${creator}')"
|
||||
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) '"
|
||||
onmouseout="this.style.backgroundColor='rgba(7, 122, 101, 0.63) '"
|
||||
>
|
||||
<button
|
||||
onclick="handleInviteMinter('${safeCreator}')"
|
||||
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)'"
|
||||
onmouseout="this.style.backgroundColor='rgba(7, 122, 101, 0.63)'"
|
||||
>
|
||||
Create Minter Invite
|
||||
</button>
|
||||
</div>
|
||||
@ -1327,17 +1342,12 @@ const featureTriggerCheck = async () => {
|
||||
|
||||
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 minterAdmins = await fetchMinterGroupAdmins()
|
||||
|
||||
let minAdminCount = 9
|
||||
if (isBlockPassed) {
|
||||
minAdminCount = Math.round(minterAdmins.length * 0.4)
|
||||
minAdminCount = Math.ceil(minterAdmins.length * 0.4)
|
||||
console.warn(`Using 40% => ${minAdminCount}`)
|
||||
}
|
||||
|
||||
@ -1378,7 +1388,7 @@ const checkAndDisplayInviteButton = async (adminYes, creator, cardIdentifier) =>
|
||||
|
||||
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")
|
||||
|
||||
if (!priorBanOrKick) {
|
||||
@ -1467,7 +1477,8 @@ const checkGroupApprovalAndCreateButton = async (address, cardIdentifier, transa
|
||||
Existing ${transactionType} Approvals: ${uniqueApprovalCount}
|
||||
</p>
|
||||
${tableHtml}
|
||||
<div id="approval-button-container-${cardIdentifier}" style="margin-top: 1em;">
|
||||
${userState.isMinterAdmin ?
|
||||
`<div id="approval-button-container-${cardIdentifier}" style="margin-top: 1em;">
|
||||
<button
|
||||
style="
|
||||
padding: 8px;
|
||||
@ -1484,7 +1495,8 @@ const checkGroupApprovalAndCreateButton = async (address, cardIdentifier, transa
|
||||
>
|
||||
Approve Invite Tx
|
||||
</button>
|
||||
</div>
|
||||
</div>`
|
||||
: ''}
|
||||
</div>
|
||||
`
|
||||
return approvalButtonHtml
|
||||
@ -1644,9 +1656,19 @@ async function buildApprovalTableHtml(approvalTxs, getNameFunc) {
|
||||
// Format the transaction timestamp
|
||||
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 `
|
||||
<tr>
|
||||
<td style="border: 1px solid rgb(255, 255, 255); padding: 4px; color: #234565">${displayName}</td>
|
||||
<tr style="${rowStyle}">
|
||||
<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>
|
||||
</tr>
|
||||
`
|
||||
@ -1659,6 +1681,8 @@ async function buildApprovalTableHtml(approvalTxs, getNameFunc) {
|
||||
// 4) Wrap the table in a container with horizontal scroll:
|
||||
// 1) max-width: 100% makes it fit the parent (card) width
|
||||
// 2) overflow-x: auto allows scrolling if the table is too wide
|
||||
|
||||
// TODO - if "Admin Name" == userState.accountName, then table row should be highlighted
|
||||
const containerHtml = `
|
||||
<div style="max-width: 100%; overflow-x: auto;">
|
||||
<table style="border: 1px solid #ccc; border-collapse: collapse; width: 100%;">
|
||||
@ -1788,6 +1812,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 -----------------------------------------------
|
||||
const createCardHTML = async (cardData, pollResults, cardIdentifier, commentCount, cardUpdatedTime, bgColor, address) => {
|
||||
const { header, content, links, creator, timestamp, poll } = cardData
|
||||
@ -1895,6 +1960,16 @@ const createCardHTML = async (cardData, pollResults, cardIdentifier, commentCoun
|
||||
<button class="no" onclick="voteNoOnPoll('${poll}')">NO</button>
|
||||
</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-container-${cardIdentifier}" class="comments-container"></div>
|
||||
<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('name:', name)
|
||||
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) {
|
||||
console.warn(`Failed to fetch name info for: ${name}, status: ${response.status}`)
|
||||
|
38
index.html
38
index.html
@ -30,7 +30,11 @@
|
||||
<link href="./assets/quill/quill.snow.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
// Define one variable for your version string:
|
||||
const Q_MINTERSHIP_VERSION = "1.03"; // Update here in the future
|
||||
</script>
|
||||
|
||||
<section data-bs-version="5.1" class="menu menu1 boldm5 cid-ttRnktJ11Q" once="menu" id="menu1-0">
|
||||
|
||||
<nav class="navbar navbar-dropdown navbar-expand-lg">
|
||||
@ -42,7 +46,7 @@
|
||||
</a>
|
||||
</span>
|
||||
<span class="navbar-caption-wrap">
|
||||
<a class="navbar-caption display-4" href="index.html">Q-Mintership (v1.02b)
|
||||
<a class="navbar-caption display-4" href="index.html"><span id="versionString1" class="navbar-caption display-4"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
@ -61,7 +65,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 v1.02b<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>
|
||||
<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>
|
||||
|
||||
@ -191,6 +195,24 @@
|
||||
|
||||
<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">
|
||||
v1.03beta 01-23-2025</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-5 card">
|
||||
<div class="text-wrapper">
|
||||
<p class="mbr-text mbr-fonts-style display-7">
|
||||
<b><u>v1.03b Fixes</u></b>- <b>Filtering issue resolved </b> - Version 1.02 had a filtering logic modification applied to add and remove admin transactions. However, it was not changed on the REMOVE filtering, so REMOVE transactions that were PENDING were showing as COMPLETE and thus the board was displaying cards as already removed when there was only a PENDING tx. This has been resolved in 1.03b.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-7 card">
|
||||
@ -536,12 +558,12 @@
|
||||
<div class="title-wrapper">
|
||||
<div class="title-wrap">
|
||||
<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.02b)</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>
|
||||
|
||||
<a class="link-wrap" href="#">
|
||||
<p class="mbr-link mbr-fonts-style display-4">Q-Mintership v1.02beta</p>
|
||||
<p class="mbr-link mbr-fonts-style display-4"><span id="versionString4" class="mbr-link mbr-fonts-style display-4"></span></p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
@ -554,6 +576,12 @@
|
||||
</div>
|
||||
</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/parallax/jarallax.js"></script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user