Compare commits
6 Commits
35e6595311
...
e0c5a09378
Author | SHA1 | Date | |
---|---|---|---|
e0c5a09378 | |||
509e3bf357 | |||
07f4fa3e6e | |||
7afa06623f | |||
51921992e2 | |||
f5ce634ff5 |
@ -708,31 +708,6 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Responsive design */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.publish-card-view {
|
|
||||||
width: 90%;
|
|
||||||
padding: 2vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.publish-card-button {
|
|
||||||
font-size: 1.8vh;
|
|
||||||
padding: 1.5vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.publish-card-form button {
|
|
||||||
font-size: 1.8vh;
|
|
||||||
padding: 1.2vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.refresh-cards-button {
|
|
||||||
border-color: white;
|
|
||||||
border-radius: 1.5vh;
|
|
||||||
background-color: black;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive Design */
|
/* Responsive Design */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.publish-card-view {
|
.publish-card-view {
|
||||||
@ -754,8 +729,14 @@ body {
|
|||||||
.refresh-cards-button {
|
.refresh-cards-button {
|
||||||
border-color: white;
|
border-color: white;
|
||||||
border-radius: 1.5vh;
|
border-radius: 1.5vh;
|
||||||
background-color: black;
|
background-color: rgba(0, 0, 0, 0.089);
|
||||||
color: white;
|
color: white;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-cards-button:hover {
|
||||||
|
background-color: rgba(35, 129, 136, 0.137);
|
||||||
|
color: rgba(90, 201, 221, 0.793);
|
||||||
}
|
}
|
||||||
/* Two cards per row on medium screens */
|
/* Two cards per row on medium screens */
|
||||||
|
|
||||||
|
@ -60,11 +60,13 @@ const loadAddRemoveAdminPage = async () => {
|
|||||||
<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="time-range-select" style="margin-left: 10px; padding: 5px; font-size: 1.25rem; color: white; background-color: black;">
|
<select id="time-range-select" style="margin-left: 10px; padding: 5px; font-size: 1.25rem; color: white; background-color: black;">
|
||||||
<option value="0">Show All</option>
|
<option value="0">All Creation Dates</option>
|
||||||
<option value="1">Last 1 day</option>
|
<option value="1">Last 1 Day</option>
|
||||||
<option value="7">Last 7 days</option>
|
<option value="7">Last 7 Days</option>
|
||||||
<option value="30" selected>Last 30 days</option>
|
<option value="30">...Within 30 Days</option>
|
||||||
<option value="90">Last 90 days</option>
|
<option value="45" selected>Published Within Last 45 Days</option>
|
||||||
|
<option value="60">...Within 60 Days</option>
|
||||||
|
<option value="90">...Within 90 Days</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div id="cards-container" class="cards-container" style="margin-top: 1rem"">
|
<div id="cards-container" class="cards-container" style="margin-top: 1rem"">
|
||||||
@ -117,6 +119,13 @@ const loadAddRemoveAdminPage = async () => {
|
|||||||
linksContainer.appendChild(newLinkInput)
|
linksContainer.appendChild(newLinkInput)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const timeRangeSelectCheckbox = document.getElementById('time-range-select')
|
||||||
|
if (timeRangeSelectCheckbox) {
|
||||||
|
timeRangeSelectCheckbox.addEventListener('change', async (event) => {
|
||||||
|
await loadCards(addRemoveIdentifierPrefix)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById("publish-card-form").addEventListener("submit", async (event) => {
|
document.getElementById("publish-card-form").addEventListener("submit", async (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
await publishARCard(addRemoveIdentifierPrefix)
|
await publishARCard(addRemoveIdentifierPrefix)
|
||||||
|
@ -84,12 +84,14 @@ const loadAdminBoardPage = async () => {
|
|||||||
<option value="most-votes">Most Votes</option>
|
<option value="most-votes">Most Votes</option>
|
||||||
</select>
|
</select>
|
||||||
<select id="time-range-select" style="margin-left: 10px; padding: 5px; font-size: 1.25rem; color: white; background-color: black;">
|
<select id="time-range-select" style="margin-left: 10px; padding: 5px; font-size: 1.25rem; color: white; background-color: black;">
|
||||||
<option value="0">Show All</option>
|
<option value="0">All Creation Dates</option>
|
||||||
<option value="1">Last 1 day</option>
|
<option value="1">Last 1 Day</option>
|
||||||
<option value="7">Last 7 days</option>
|
<option value="7">Last 7 Days</option>
|
||||||
<option value="30" selected>Last 30 days</option>
|
<option value="30">...Within 30 Days</option>
|
||||||
<option value="90">Last 90 days</option>
|
<option value="45" selected>Published Within Last 45 Days</option>
|
||||||
</select>
|
<option value="60">...Within 60 Days</option>
|
||||||
|
<option value="90">...Within 90 Days</option>
|
||||||
|
</select>
|
||||||
<div class="show-card-checkbox" style="margin-top: 1em;">
|
<div class="show-card-checkbox" style="margin-top: 1em;">
|
||||||
<input type="checkbox" id="admin-show-hidden-checkbox" name="adminHidden" />
|
<input type="checkbox" id="admin-show-hidden-checkbox" name="adminHidden" />
|
||||||
<label for="admin-show-hidden-checkbox">Show User-Hidden Cards?</label>
|
<label for="admin-show-hidden-checkbox">Show User-Hidden Cards?</label>
|
||||||
@ -1078,7 +1080,7 @@ const createRemoveButtonHtml = (name, cardIdentifier) => {
|
|||||||
|
|
||||||
const handleKickMinter = async (minterName) => {
|
const handleKickMinter = async (minterName) => {
|
||||||
try {
|
try {
|
||||||
isAddress = await getAddressInfo(minterName)
|
let isAddress = await getAddressInfo(minterName)
|
||||||
|
|
||||||
// Optional block check
|
// Optional block check
|
||||||
let txGroupId = 0
|
let txGroupId = 0
|
||||||
@ -1091,7 +1093,7 @@ const handleKickMinter = async (minterName) => {
|
|||||||
|
|
||||||
// Get the minter address from name info
|
// Get the minter address from name info
|
||||||
let minterAddress
|
let minterAddress
|
||||||
if (!isAddress){
|
if (!isAddress.address || !isAddress.address != minterName){
|
||||||
const minterNameInfo = await getNameInfo(minterName)
|
const minterNameInfo = await getNameInfo(minterName)
|
||||||
minterAddress = minterNameInfo?.owner
|
minterAddress = minterNameInfo?.owner
|
||||||
} else {
|
} else {
|
||||||
@ -1107,7 +1109,7 @@ const handleKickMinter = async (minterName) => {
|
|||||||
const reason = 'Kicked by Minter Admins'
|
const reason = 'Kicked by Minter Admins'
|
||||||
const fee = 0.01
|
const fee = 0.01
|
||||||
|
|
||||||
const rawKickTransaction = await createGroupKickTransaction(minterAddress, adminPublicKey, 694, minterAddress, reason, txGroupId, fee)
|
const rawKickTransaction = await createGroupKickTransaction(adminPublicKey, 694, minterAddress, reason, txGroupId, fee)
|
||||||
|
|
||||||
const signedKickTransaction = await qortalRequest({
|
const signedKickTransaction = await qortalRequest({
|
||||||
action: "SIGN_TRANSACTION",
|
action: "SIGN_TRANSACTION",
|
||||||
@ -1138,7 +1140,7 @@ const handleKickMinter = async (minterName) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleBanMinter = async (minterName) => {
|
const handleBanMinter = async (minterName) => {
|
||||||
isAddress = await getAddressInfo(minterName)
|
let isAddress = await getAddressInfo(minterName)
|
||||||
try {
|
try {
|
||||||
let txGroupId = 0
|
let txGroupId = 0
|
||||||
// const { height: currentHeight } = await getLatestBlockInfo()
|
// const { height: currentHeight } = await getLatestBlockInfo()
|
||||||
@ -1151,9 +1153,9 @@ const handleBanMinter = async (minterName) => {
|
|||||||
txGroupId = 694
|
txGroupId = 694
|
||||||
}
|
}
|
||||||
let minterAddress
|
let minterAddress
|
||||||
if (!isAddress) {
|
if (!isAddress.address || !isAddress.address != minterName){
|
||||||
const minterNameInfo = await getNameInfo(minterName)
|
const minterNameInfo = await getNameInfo(minterName)
|
||||||
const minterAddress = minterNameInfo?.owner
|
minterAddress = minterNameInfo?.owner
|
||||||
} else {
|
} else {
|
||||||
minterAddress = minterName
|
minterAddress = minterName
|
||||||
}
|
}
|
||||||
|
@ -25,42 +25,99 @@ const loadMinterBoardPage = async () => {
|
|||||||
const publishButtonColor = '#527c9d'
|
const publishButtonColor = '#527c9d'
|
||||||
const minterBoardNameColor = '#527c9d'
|
const minterBoardNameColor = '#527c9d'
|
||||||
mainContent.innerHTML = `
|
mainContent.innerHTML = `
|
||||||
<div class="minter-board-main" style="padding: 20px; text-align: center;">
|
<div class="minter-board-main" style="padding: 0.5vh; text-align: center;">
|
||||||
<h1 style="color: ${minterBoardNameColor};">Minter Board</h1>
|
|
||||||
<p style="font-size: 1.25em;"> Publish a Minter Card with Information, and obtain and view the support of the community. Welcome to the Minter Board!</p>
|
<!-- Board Title + Intro -->
|
||||||
<button id="publish-card-button" class="publish-card-button" style="margin: 20px; padding: 10px; background-color: ${publishButtonColor}">Publish Minter Card</button>
|
<h1 style="color: #527c9d;">The Minter Board</h1>
|
||||||
<button id="refresh-cards-button" class="refresh-cards-button" style="padding: 10px;">Refresh Cards</button>
|
<p style="font-size: 1.2em; color:rgb(85, 119, 101)">
|
||||||
<select id="sort-select" style="margin-left: 10px; padding: 5px; font-size: 1.25rem; color:rgb(38, 106, 106); background-color: black;">
|
The Minter Board is where Minting Rights are Delegated.
|
||||||
<option value="newest" selected>Sort by Date</option>
|
</p>
|
||||||
<option value="name">Sort by Name</option>
|
<p style="font-size: 1.1em; color:rgb(85, 119, 119)">
|
||||||
<option value="recent-comments">Newest Comments</option>
|
To obtain minting rights, click 'PUBLISH CARD' and create your card. A subsequent vote will approve/deny your card.
|
||||||
<option value="least-votes">Least Votes</option>
|
</p>
|
||||||
<option value="most-votes">Most Votes</option>
|
<p>
|
||||||
</select>
|
After your card has received the necessary invite, return to the card and click the Join Group button to join the MINTER group.
|
||||||
<select id="time-range-select" style="margin-left: 10px; padding: 5px; font-size: 1.25rem; color: white; background-color: black;">
|
(A Detailed how-to guide will be coming soon.)
|
||||||
<option value="0">Show All</option>
|
</p>
|
||||||
<option value="1">Last 1 day</option>
|
|
||||||
<option value="7">Last 7 days</option>
|
<div class="card-display-options">
|
||||||
<option value="30" selected>Last 30 days</option>
|
<!-- Centered heading -->
|
||||||
<option value="90">Last 90 days</option>
|
<h4 class="options-heading"style="color: #527c9d;">CARD DISPLAY OPTIONS</h4>
|
||||||
</select>
|
|
||||||
<div id="cards-container" class="cards-container" style="margin-top: 20px;"></div>
|
<!-- A flex container for all the controls (sort, time range, checkbox) -->
|
||||||
<div id="publish-card-view" class="publish-card-view" style="display: none; text-align: left; padding: 20px;">
|
<div class="options-row">
|
||||||
<form id="publish-card-form" class="publish-card-form">
|
<!-- Sort by -->
|
||||||
<h3>Create or Update Your Card</h3>
|
<label for="sort-select" class="options-label">Sort By:</label>
|
||||||
<label for="card-header">Header:</label>
|
<select id="sort-select" class="options-select">
|
||||||
<input type="text" id="card-header" maxlength="100" placeholder="Enter card header" required>
|
<option value="newest" selected>Date</option>
|
||||||
<label for="card-content">Content:</label>
|
<option value="name">Name</option>
|
||||||
<textarea id="card-content" placeholder="Enter detailed information about why you would like to be a minter... the more the better, and links to things you have published on QDN will help a lot! Give the Minter Admins things to make decisions by!" required></textarea>
|
<option value="recent-comments">Newest Comments</option>
|
||||||
<label for="card-links">Links (qortal://...):</label>
|
<option value="least-votes">Least Votes</option>
|
||||||
<div id="links-container">
|
<option value="most-votes">Most Votes</option>
|
||||||
<input type="text" class="card-link" placeholder="Enter QDN link">
|
</select>
|
||||||
|
|
||||||
|
<!-- Time range -->
|
||||||
|
<label for="time-range-select" class="options-label">Show Cards:</label>
|
||||||
|
<select id="time-range-select" class="options-select">
|
||||||
|
<option value="0">Show ALL Cards Published</option>
|
||||||
|
<option value="1">...Within Last 1 Day</option>
|
||||||
|
<option value="7">...Within Last 7 Days</option>
|
||||||
|
<option value="30">...Within 30 Days</option>
|
||||||
|
<option value="45" selected>Published Within Last 45 Days</option>
|
||||||
|
<option value="60">...Within 60 Days</option>
|
||||||
|
<option value="90">...Within 90 Days</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- Show existing checkbox -->
|
||||||
|
<label class="options-check">
|
||||||
|
<input type="checkbox" id="show-existing-checkbox" />
|
||||||
|
Show Existing Minter Cards (History)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Card counter heading centered, with actual counter below if desired -->
|
||||||
|
<div style="margin-bottom: 1em;">
|
||||||
|
<div style="text-align: center; margin-top: 0.5em;">
|
||||||
|
<span id="board-card-counter" style="font-size: 1rem; color:rgb(153, 203, 204); padding: 0.5em;">
|
||||||
|
<!-- e.g. "5 cards found" -->
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" id="add-link-button">Add Another Link</button>
|
</div>
|
||||||
<button type="submit" id="submit-publish-button">Publish Card</button>
|
|
||||||
<button type="button" id="cancel-publish-button">Cancel</button>
|
<!-- Row for Publish / Refresh actions -->
|
||||||
</form>
|
<div class="card-actions" style="margin-bottom: 1em;">
|
||||||
</div>
|
<button id="publish-card-button" class="publish-card-button">
|
||||||
|
PUBLISH CARD
|
||||||
|
</button>
|
||||||
|
<button id="refresh-cards-button" class="refresh-cards-button"
|
||||||
|
style="padding: 1vh;">
|
||||||
|
REFRESH CARDS
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Container for displayed cards -->
|
||||||
|
<div id="cards-container" class="cards-container" style="margin-top: 2vh;"></div>
|
||||||
|
|
||||||
|
<!-- Hidden Publish Card Form -->
|
||||||
|
<div id="publish-card-view" class="publish-card-view" style="display: none; text-align: left; padding: 2vh;">
|
||||||
|
<form id="publish-card-form" class="publish-card-form">
|
||||||
|
<h3>Create or Update Your Card</h3>
|
||||||
|
<label for="card-header">Header:</label>
|
||||||
|
<input type="text" id="card-header" maxlength="100" placeholder="Enter card header" required>
|
||||||
|
|
||||||
|
<label for="card-content">Content:</label>
|
||||||
|
<textarea id="card-content" placeholder="Enter detailed information about why you would like to be a minter... the more the better..." required>
|
||||||
|
</textarea>
|
||||||
|
|
||||||
|
<label for="card-links">Links (qortal://...):</label>
|
||||||
|
<div id="links-container">
|
||||||
|
<input type="text" class="card-link" placeholder="Enter QDN link">
|
||||||
|
</div>
|
||||||
|
<button type="button" id="add-link-button">Add Another Link</button>
|
||||||
|
<button type="submit" id="submit-publish-button">Publish Card</button>
|
||||||
|
<button type="button" id="cancel-publish-button">Cancel</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
document.body.appendChild(mainContent)
|
document.body.appendChild(mainContent)
|
||||||
@ -144,6 +201,13 @@ const loadMinterBoardPage = async () => {
|
|||||||
await loadCards(minterCardIdentifierPrefix)
|
await loadCards(minterCardIdentifierPrefix)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const showExistingCardsCheckbox = document.getElementById('show-existing-checkbox')
|
||||||
|
if (showExistingCardsCheckbox) {
|
||||||
|
showExistingCardsCheckbox.addEventListener('change', async (event) => {
|
||||||
|
await loadCards(minterCardIdentifierPrefix)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
await featureTriggerCheck()
|
await featureTriggerCheck()
|
||||||
await loadCards(minterCardIdentifierPrefix)
|
await loadCards(minterCardIdentifierPrefix)
|
||||||
}
|
}
|
||||||
@ -367,6 +431,12 @@ 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>Loading cards...</p>"
|
||||||
|
const counterSpan = document.getElementById("board-card-counter")
|
||||||
|
|
||||||
|
if (counterSpan) {
|
||||||
|
// Clear or show "Loading..."
|
||||||
|
counterSpan.textContent = "(loading...)"
|
||||||
|
}
|
||||||
|
|
||||||
if (cardIdentifierPrefix.startsWith("QM-AR-card")) {
|
if (cardIdentifierPrefix.startsWith("QM-AR-card")) {
|
||||||
isARBoard = true
|
isARBoard = true
|
||||||
@ -375,6 +445,9 @@ const loadCards = async (cardIdentifierPrefix) => {
|
|||||||
let afterTime = 0
|
let afterTime = 0
|
||||||
const timeRangeSelect = document.getElementById("time-range-select")
|
const timeRangeSelect = document.getElementById("time-range-select")
|
||||||
|
|
||||||
|
const showExistingCheckbox = document.getElementById("show-existing-checkbox")
|
||||||
|
const showExisting = showExistingCheckbox && showExistingCheckbox.checked
|
||||||
|
|
||||||
if (timeRangeSelect) {
|
if (timeRangeSelect) {
|
||||||
const days = parseInt(timeRangeSelect.value, 10)
|
const days = parseInt(timeRangeSelect.value, 10)
|
||||||
if (days > 0) {
|
if (days > 0) {
|
||||||
@ -440,6 +513,7 @@ const loadCards = async (cardIdentifierPrefix) => {
|
|||||||
// else 'newest' => do nothing (already sorted newest-first by your process functions).
|
// else 'newest' => do nothing (already sorted newest-first by your process functions).
|
||||||
// Create the 'finalCardsArray' that includes the data, etc.
|
// Create the 'finalCardsArray' that includes the data, etc.
|
||||||
let finalCardsArray = []
|
let finalCardsArray = []
|
||||||
|
let alreadyMinterCards = []
|
||||||
cardsContainer.innerHTML = ''
|
cardsContainer.innerHTML = ''
|
||||||
for (const card of finalCards) {
|
for (const card of finalCards) {
|
||||||
try {
|
try {
|
||||||
@ -477,8 +551,14 @@ const loadCards = async (cardIdentifierPrefix) => {
|
|||||||
} else {
|
} else {
|
||||||
const isAlreadyMinter = await verifyMinter(cardDataResponse.creator)
|
const isAlreadyMinter = await verifyMinter(cardDataResponse.creator)
|
||||||
if (isAlreadyMinter) {
|
if (isAlreadyMinter) {
|
||||||
console.warn(`card IS ALREADY a minter, NOT displaying following identifier on the MinterBoard: ${card.identifier}`)
|
console.warn(`card IS ALREADY a minter, adding to alreadyMinterCards array: ${card.identifier}`)
|
||||||
removeSkeleton(card.identifier)
|
removeSkeleton(card.identifier)
|
||||||
|
alreadyMinterCards.push({
|
||||||
|
...card,
|
||||||
|
cardDataResponse,
|
||||||
|
pollPublisherAddress,
|
||||||
|
cardPublisherAddress
|
||||||
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -489,6 +569,12 @@ const loadCards = async (cardIdentifierPrefix) => {
|
|||||||
pollPublisherAddress,
|
pollPublisherAddress,
|
||||||
cardPublisherAddress,
|
cardPublisherAddress,
|
||||||
})
|
})
|
||||||
|
if (counterSpan) {
|
||||||
|
const displayedCount = finalCardsArray.length
|
||||||
|
const alreadyMinterCount = alreadyMinterCards.length
|
||||||
|
// If you want to show both
|
||||||
|
counterSpan.textContent = `(${displayedCount} cards, ${alreadyMinterCount} existingMinters)`
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Error preparing card ${card.identifier}`, err)
|
console.error(`Error preparing card ${card.identifier}`, err)
|
||||||
removeSkeleton(card.identifier)
|
removeSkeleton(card.identifier)
|
||||||
@ -531,9 +617,39 @@ const loadCards = async (cardIdentifierPrefix) => {
|
|||||||
replaceSkeleton(cardObj.identifier, finalCardHTML)
|
replaceSkeleton(cardObj.identifier, finalCardHTML)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showExisting && alreadyMinterCards.length > 0) {
|
||||||
|
console.warn(`Rendering Existing Minter cards because user selected showExisting`)
|
||||||
|
|
||||||
|
for (const mintedCardObj of alreadyMinterCards) {
|
||||||
|
const skeletonHTML = createSkeletonCardHTML(mintedCardObj.identifier)
|
||||||
|
cardsContainer.insertAdjacentHTML("beforeend", skeletonHTML)
|
||||||
|
|
||||||
|
const pollResults = await fetchPollResults(mintedCardObj.cardDataResponse.poll)
|
||||||
|
const commentCount = await countComments(mintedCardObj.identifier)
|
||||||
|
const cardUpdatedTime = mintedCardObj.updated || null
|
||||||
|
const bgColor = generateDarkPastelBackgroundBy(mintedCardObj.name)
|
||||||
|
const isExistingMinter = true
|
||||||
|
|
||||||
|
const finalCardHTML = await createCardHTML(
|
||||||
|
mintedCardObj.cardDataResponse,
|
||||||
|
pollResults,
|
||||||
|
mintedCardObj.identifier,
|
||||||
|
commentCount,
|
||||||
|
cardUpdatedTime,
|
||||||
|
bgColor,
|
||||||
|
mintedCardObj.cardPublisherAddress,
|
||||||
|
isExistingMinter
|
||||||
|
)
|
||||||
|
replaceSkeleton(mintedCardObj.identifier, finalCardHTML)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error loading cards:", error)
|
console.error("Error loading cards:", error)
|
||||||
cardsContainer.innerHTML = "<p>Failed to load cards.</p>"
|
cardsContainer.innerHTML = "<p>Failed to load cards.</p>"
|
||||||
|
if (counterSpan) {
|
||||||
|
counterSpan.textContent = "(error loading)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -750,7 +866,6 @@ const loadCardIntoForm = async (cardData) => {
|
|||||||
|
|
||||||
// Main function to publish a new Minter Card -----------------------------------------------
|
// Main function to publish a new Minter Card -----------------------------------------------
|
||||||
const publishCard = async (cardIdentifierPrefix) => {
|
const publishCard = async (cardIdentifierPrefix) => {
|
||||||
|
|
||||||
const minterGroupData = await fetchMinterGroupMembers()
|
const minterGroupData = await fetchMinterGroupMembers()
|
||||||
const minterGroupAddresses = minterGroupData.map(m => m.member)
|
const minterGroupAddresses = minterGroupData.map(m => m.member)
|
||||||
const userAddress = userState.accountAddress
|
const userAddress = userState.accountAddress
|
||||||
@ -759,6 +874,7 @@ const publishCard = async (cardIdentifierPrefix) => {
|
|||||||
alert("You are already a Minter and cannot publish a new card!")
|
alert("You are already a Minter and cannot publish a new card!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const header = document.getElementById("card-header").value.trim()
|
const header = document.getElementById("card-header").value.trim()
|
||||||
const content = document.getElementById("card-content").value.trim()
|
const content = document.getElementById("card-content").value.trim()
|
||||||
const links = Array.from(document.querySelectorAll(".card-link"))
|
const links = Array.from(document.querySelectorAll(".card-link"))
|
||||||
@ -770,8 +886,27 @@ const publishCard = async (cardIdentifierPrefix) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const cardIdentifier = isExistingCard ? existingCardIdentifier : `${cardIdentifierPrefix}-${await uid()}`
|
if (isExistingCard) {
|
||||||
const pollName = `${cardIdentifier}-poll`
|
if (!existingCardData || Object.keys(existingCardData).length === 0) {
|
||||||
|
const fetched = await fetchExistingCard(cardIdentifierPrefix)
|
||||||
|
if (fetched) {
|
||||||
|
existingCardData = fetched
|
||||||
|
} else {
|
||||||
|
console.warn("fetchExistingCard returned null. Possibly no existing card found.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cardIdentifier = isExistingCard && existingCardIdentifier
|
||||||
|
? existingCardIdentifier
|
||||||
|
: `${cardIdentifierPrefix}-${await uid()}`
|
||||||
|
|
||||||
|
let existingPollName
|
||||||
|
if (existingCardData && existingCardData.poll) {
|
||||||
|
existingPollName = existingCardData.poll
|
||||||
|
}
|
||||||
|
|
||||||
|
const pollName = existingPollName || `${cardIdentifier}-poll`
|
||||||
const pollDescription = `Mintership Board Poll for ${userState.accountName}`
|
const pollDescription = `Mintership Board Poll for ${userState.accountName}`
|
||||||
|
|
||||||
const cardData = {
|
const cardData = {
|
||||||
@ -781,16 +916,16 @@ const publishCard = async (cardIdentifierPrefix) => {
|
|||||||
creator: userState.accountName,
|
creator: userState.accountName,
|
||||||
creatorAddress: userState.accountAddress,
|
creatorAddress: userState.accountAddress,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
poll: pollName,
|
poll: pollName // either the existing poll or a new one
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let base64CardData = await objectToBase64(cardData)
|
let base64CardData = await objectToBase64(cardData)
|
||||||
if (!base64CardData) {
|
if (!base64CardData) {
|
||||||
console.log(`initial base64 object creation with objectToBase64 failed, using btoa...`)
|
console.log(`initial base64 object creation with objectToBase64 failed, using btoa...`)
|
||||||
base64CardData = btoa(JSON.stringify(cardData))
|
base64CardData = btoa(JSON.stringify(cardData))
|
||||||
}
|
}
|
||||||
|
|
||||||
await qortalRequest({
|
await qortalRequest({
|
||||||
action: "PUBLISH_QDN_RESOURCE",
|
action: "PUBLISH_QDN_RESOURCE",
|
||||||
name: userState.accountName,
|
name: userState.accountName,
|
||||||
@ -799,7 +934,7 @@ const publishCard = async (cardIdentifierPrefix) => {
|
|||||||
data64: base64CardData,
|
data64: base64CardData,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!isExistingCard){
|
if (!isExistingCard || !existingPollName) {
|
||||||
await qortalRequest({
|
await qortalRequest({
|
||||||
action: "CREATE_POLL",
|
action: "CREATE_POLL",
|
||||||
pollName,
|
pollName,
|
||||||
@ -807,26 +942,33 @@ const publishCard = async (cardIdentifierPrefix) => {
|
|||||||
pollOptions: ['Yes, No'],
|
pollOptions: ['Yes, No'],
|
||||||
pollOwnerAddress: userState.accountAddress,
|
pollOwnerAddress: userState.accountAddress,
|
||||||
})
|
})
|
||||||
alert("Card and poll published successfully!")
|
if (!isExistingCard) {
|
||||||
|
alert("Card and poll published successfully!")
|
||||||
|
} else {
|
||||||
|
alert("Existing card updated, and new poll created (since existing poll was missing)!")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Card updated successfully! (No poll updates possible)")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isExistingCard){
|
if (isExistingCard) {
|
||||||
alert("Card Updated Successfully! (No poll updates possible)")
|
|
||||||
isExistingCard = false
|
isExistingCard = false
|
||||||
|
existingCardData = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById("publish-card-form").reset()
|
document.getElementById("publish-card-form").reset()
|
||||||
document.getElementById("publish-card-view").style.display = "none"
|
document.getElementById("publish-card-view").style.display = "none"
|
||||||
document.getElementById("cards-container").style.display = "flex"
|
document.getElementById("cards-container").style.display = "flex"
|
||||||
|
|
||||||
await loadCards(minterCardIdentifierPrefix)
|
await loadCards(minterCardIdentifierPrefix)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
||||||
console.error("Error publishing card or poll:", error)
|
console.error("Error publishing card or poll:", error)
|
||||||
alert("Failed to publish card and poll.")
|
alert("Failed to publish card and poll.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let globalVoterMap = new Map()
|
let globalVoterMap = new Map()
|
||||||
|
|
||||||
const processPollData= async (pollData, minterGroupMembers, minterAdmins, creator, cardIdentifier) => {
|
const processPollData= async (pollData, minterGroupMembers, minterAdmins, creator, cardIdentifier) => {
|
||||||
@ -1193,6 +1335,7 @@ const toggleComments = async (cardIdentifier) => {
|
|||||||
const commentButton = document.getElementById(`comment-button-${cardIdentifier}`)
|
const commentButton = document.getElementById(`comment-button-${cardIdentifier}`)
|
||||||
|
|
||||||
if (!commentsSection || !commentButton) return
|
if (!commentsSection || !commentButton) return
|
||||||
|
|
||||||
const count = commentButton.dataset.commentCount
|
const count = commentButton.dataset.commentCount
|
||||||
const isHidden = (commentsSection.style.display === 'none' || !commentsSection.style.display)
|
const isHidden = (commentsSection.style.display === 'none' || !commentsSection.style.display)
|
||||||
|
|
||||||
@ -1469,7 +1612,7 @@ const checkAndDisplayInviteButton = async (adminYes, creator, cardIdentifier) =>
|
|||||||
// fetch all final KICK/BAN tx
|
// fetch all final KICK/BAN tx
|
||||||
const { finalKickTxs, finalBanTxs } = await fetchAllKickBanTxData()
|
const { finalKickTxs, finalBanTxs } = await fetchAllKickBanTxData()
|
||||||
const { finalInviteTxs, pendingInviteTxs } = await fetchAllInviteTransactions()
|
const { finalInviteTxs, pendingInviteTxs } = await fetchAllInviteTransactions()
|
||||||
// check if there's a final (non-pending) KICK or BAN for this user
|
// check if there's a KICK or BAN for this user.
|
||||||
const priorKick = finalKickTxs.some(tx => tx.member === minterAddress)
|
const priorKick = finalKickTxs.some(tx => tx.member === minterAddress)
|
||||||
const priorBan = finalBanTxs.some(tx => tx.offender === minterAddress)
|
const priorBan = finalBanTxs.some(tx => tx.offender === minterAddress)
|
||||||
const existingInvite = finalInviteTxs.some(tx => tx.invitee === minterAddress)
|
const existingInvite = finalInviteTxs.some(tx => tx.invitee === minterAddress)
|
||||||
@ -1480,10 +1623,12 @@ const checkAndDisplayInviteButton = async (adminYes, creator, cardIdentifier) =>
|
|||||||
// build the normal invite button & groupApprovalHtml
|
// build the normal invite button & groupApprovalHtml
|
||||||
let inviteButtonHtml = ""
|
let inviteButtonHtml = ""
|
||||||
if (existingInvite || pendingInvite){
|
if (existingInvite || pendingInvite){
|
||||||
console.warn(`There is an EXISTING INVITE for this user! No invite button being created... existing: (${existingInvite}, pending: ${pendingInvite})`)
|
console.warn(`There is an EXISTING or PENDING INVITE for this user! No invite button being created... existing: (${existingInvite}, pending: ${pendingInvite})`)
|
||||||
inviteButtonHtml = ''
|
inviteButtonHtml = ''
|
||||||
|
} else {
|
||||||
|
inviteButtonHtml = isSomeTypaAdmin ? createInviteButtonHtml(creator, cardIdentifier) : ""
|
||||||
}
|
}
|
||||||
inviteButtonHtml = isSomeTypaAdmin ? createInviteButtonHtml(creator, cardIdentifier) : ""
|
|
||||||
const groupApprovalHtml = await checkGroupApprovalAndCreateButton(minterAddress, cardIdentifier, "GROUP_INVITE")
|
const groupApprovalHtml = await checkGroupApprovalAndCreateButton(minterAddress, cardIdentifier, "GROUP_INVITE")
|
||||||
|
|
||||||
// if user had no prior KICK/BAN
|
// if user had no prior KICK/BAN
|
||||||
@ -1903,7 +2048,7 @@ const getNewestCommentTimestamp = async (cardIdentifier) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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, isExistingMinter=false) => {
|
||||||
const { header, content, links, creator, creatorAddress, timestamp, poll } = cardData
|
const { header, content, links, creator, creatorAddress, timestamp, poll } = cardData
|
||||||
const formattedDate = cardUpdatedTime ? new Date(cardUpdatedTime).toLocaleString() : new Date(timestamp).toLocaleString()
|
const formattedDate = cardUpdatedTime ? new Date(cardUpdatedTime).toLocaleString() : new Date(timestamp).toLocaleString()
|
||||||
const avatarHtml = await getMinterAvatar(creator)
|
const avatarHtml = await getMinterAvatar(creator)
|
||||||
@ -1919,7 +2064,7 @@ const createCardHTML = async (cardData, pollResults, cardIdentifier, commentCoun
|
|||||||
createModal('links')
|
createModal('links')
|
||||||
createModal('poll-details')
|
createModal('poll-details')
|
||||||
|
|
||||||
const inviteButtonHtml = await checkAndDisplayInviteButton(adminYes, creator, cardIdentifier)
|
const inviteButtonHtml = isExistingMinter ? "" : await checkAndDisplayInviteButton(adminYes, creator, cardIdentifier)
|
||||||
let inviteHtmlAdd = (inviteButtonHtml) ? inviteButtonHtml : ''
|
let inviteHtmlAdd = (inviteButtonHtml) ? inviteButtonHtml : ''
|
||||||
|
|
||||||
let finalBgColor = bgColor
|
let finalBgColor = bgColor
|
||||||
@ -1935,6 +2080,9 @@ const createCardHTML = async (cardData, pollResults, cardIdentifier, commentCoun
|
|||||||
finalBgColor = "rgba(1, 65, 39, 0.41)"; // or any green you want
|
finalBgColor = "rgba(1, 65, 39, 0.41)"; // or any green you want
|
||||||
} else if (userVote === 1) {
|
} else if (userVote === 1) {
|
||||||
finalBgColor = "rgba(107, 3, 3, 0.3)"; // or any red you want
|
finalBgColor = "rgba(107, 3, 3, 0.3)"; // or any red you want
|
||||||
|
} else if (isExistingMinter){
|
||||||
|
finalBgColor = "rgb(99, 99, 99)"
|
||||||
|
invitedText = `<h4 style="color:rgb(135, 55, 16); margin-bottom: 0.5em;">EXISTING MINTER</h4>`
|
||||||
} else if (hasMinterInvite) {
|
} else if (hasMinterInvite) {
|
||||||
// If so, override background color & add an "INVITED" label
|
// If so, override background color & add an "INVITED" label
|
||||||
finalBgColor = "black";
|
finalBgColor = "black";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const Q_MINTERSHIP_VERSION = "1.06"
|
const Q_MINTERSHIP_VERSION = "1.06.4"
|
||||||
|
|
||||||
const messageIdentifierPrefix = `mintership-forum-message`
|
const messageIdentifierPrefix = `mintership-forum-message`
|
||||||
const messageAttachmentIdentifierPrefix = `mintership-forum-attachment`
|
const messageAttachmentIdentifierPrefix = `mintership-forum-attachment`
|
||||||
|
@ -224,6 +224,12 @@ const getUserAddress = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getAddressInfo = async (address) => {
|
const getAddressInfo = async (address) => {
|
||||||
|
const qortalAddressPattern = /^Q[A-Za-z0-9]{33}$/ // Q + 33 almum = 34 total length
|
||||||
|
|
||||||
|
if (!qortalAddressPattern.test(address)) {
|
||||||
|
console.warn(`Not a valid Qortal address format, returning same thing that was passed to not break other functions: ${address}`)
|
||||||
|
return address
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const response = await fetch (`${baseUrl}/addresses/${address}`, {
|
const response = await fetch (`${baseUrl}/addresses/${address}`, {
|
||||||
headers: { 'Accept': 'application/json' },
|
headers: { 'Accept': 'application/json' },
|
||||||
@ -786,18 +792,20 @@ const searchSimple = async (service, identifier, name, limit=1500, offset=0, roo
|
|||||||
|
|
||||||
if (name && !identifier && !room) {
|
if (name && !identifier && !room) {
|
||||||
console.log('name only searchSimple', name)
|
console.log('name only searchSimple', name)
|
||||||
urlSuffix = `service=${service}&name=${name}&limit=${limit}&prefix=true&reverse=${reverse}`
|
urlSuffix = `service=${service}&name=${name}&limit=${limit}&prefix=true&reverse=${reverse}&after=${after}`
|
||||||
|
console.log(`urlSuffix used: ${urlSuffix}`)
|
||||||
|
|
||||||
} else if (!name && identifier && !room) {
|
} else if (!name && identifier && !room) {
|
||||||
console.log('identifier only searchSimple', identifier)
|
console.log('identifier only searchSimple', identifier)
|
||||||
urlSuffix = `service=${service}&identifier=${identifier}&limit=${limit}&prefix=true&reverse=${reverse}`
|
urlSuffix = `service=${service}&identifier=${identifier}&limit=${limit}&prefix=true&reverse=${reverse}&after=${after}`
|
||||||
|
console.log(`urlSuffix used: ${urlSuffix}`)
|
||||||
|
|
||||||
} else if (!name && !identifier && !room) {
|
} else if (!name && !identifier && !room) {
|
||||||
console.error(`name: ${name} AND identifier: ${identifier} not passed. Must include at least one...`)
|
console.error(`name: ${name} AND identifier: ${identifier} not passed. Must include at least one...`)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.log(`final searchSimple params = service: '${service}', identifier: '${identifier}', name: '${name}', limit: '${limit}', offset: '${offset}', room: '${room}', reverse: '${reverse}'`)
|
console.log(`final searchSimple params = service: '${service}', identifier: '${identifier}', name: '${name}', limit: '${limit}', offset: '${offset}', room: '${room}', reverse: '${reverse}', after: ${after}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(`${baseUrl}/arbitrary/resources/searchsimple?${urlSuffix}`, {
|
const response = await fetch(`${baseUrl}/arbitrary/resources/searchsimple?${urlSuffix}`, {
|
||||||
@ -1395,16 +1403,16 @@ const createGroupInviteTransaction = async (recipientAddress, adminPublicKey, gr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const createGroupKickTransaction = async (recipientAddress, adminPublicKey, groupId=694, member, reason='Kicked by admins', txGroupId, fee) => {
|
const createGroupKickTransaction = async (adminPublicKey, groupId=694, member, reason='Kicked by admins', txGroupId=694, fee=0.01) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch account reference correctly
|
// Fetch account reference correctly
|
||||||
const accountInfo = await getAddressInfo(recipientAddress)
|
const accountInfo = await getAddressInfo(member)
|
||||||
const accountReference = accountInfo.reference
|
const accountReference = accountInfo.reference
|
||||||
|
|
||||||
// Validate inputs before making the request
|
// Validate inputs before making the request
|
||||||
if (!adminPublicKey || !accountReference || !recipientAddress) {
|
if (!adminPublicKey || !accountReference || !member) {
|
||||||
throw new Error("Missing required parameters for group invite transaction.")
|
throw new Error("Missing required parameters for group kick transaction.")
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
@ -1412,11 +1420,10 @@ const createGroupKickTransaction = async (recipientAddress, adminPublicKey, grou
|
|||||||
reference: accountReference,
|
reference: accountReference,
|
||||||
fee,
|
fee,
|
||||||
txGroupId,
|
txGroupId,
|
||||||
recipient: null,
|
|
||||||
adminPublicKey,
|
adminPublicKey,
|
||||||
groupId: groupId,
|
groupId,
|
||||||
member: member || recipientAddress,
|
member,
|
||||||
reason: reason
|
reason
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Sending GROUP_KICK transaction payload:", payload)
|
console.log("Sending GROUP_KICK transaction payload:", payload)
|
||||||
|
@ -141,7 +141,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-12 col-lg-4">
|
<div class="col-12 col-lg-4">
|
||||||
<div class="card-wrapper" style="justify-content:center; align: center; align-text: center;">
|
<div class="card-wrapper" style="justify-content:center;">
|
||||||
<div class="icon-wrapper">
|
<div class="icon-wrapper">
|
||||||
<span class="mbr-iconfont mbr-iconfont-btn mbri-file" style="color:aliceblue;"></span>
|
<span class="mbr-iconfont mbr-iconfont-btn mbri-file" style="color:aliceblue;"></span>
|
||||||
</div>
|
</div>
|
||||||
@ -200,14 +200,14 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-lg-7 card">
|
<div class="col-12 col-lg-7 card">
|
||||||
<div class="title-wrapper">
|
<div class="title-wrapper">
|
||||||
<h2 class="mbr-section-title mbr-fonts-style display-2">
|
<h2 class="mbr-section-title mbr-fonts-style display-2 version">
|
||||||
v1.06beta 01-31-2025</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-lg-5 card">
|
<div class="col-12 col-lg-5 card">
|
||||||
<div class="text-wrapper">
|
<div class="text-wrapper">
|
||||||
<p class="mbr-text mbr-fonts-style display-7">
|
<p class="mbr-text mbr-fonts-style display-7">
|
||||||
<b><u>v1.06b Fixes</u></b>- <b>EMERGENCY UPDATE </b> - See post in the <a href="MINTERSHIP-FORUM">FORUM</a> for RELEASE NOTES, This is an emergency update that is meant to prevent the issue that took place yesterday and ended up stalling quite a few nodes. This means that Q-Mintership should be the ONLY APP UTILIZED FOR THE FUNCTIONALITY IT PROVIDES.
|
<b class="version"><u>v1.06.4b</u></b>- <b>various improvements</b> - See post in the <a href="MINTERSHIP-FORUM">FORUM</a> for RELEASE NOTES.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user