let currentMinterToolPage = 'overview'; // Track the current page const loadMinterAdminToolsPage = async () => { // Remove all body content except for menu elements const bodyChildren = document.body.children; for (let i = bodyChildren.length - 1; i >= 0; i--) { const child = bodyChildren[i]; if (!child.classList.contains('menu')) { child.remove() } } const avatarUrl = `/arbitrary/THUMBNAIL/${userState.accountName}/qortal_avatar` // Set the background image directly from a file const mainContent = document.createElement('div') // In your 'AdminTools' code mainContent.innerHTML = `

Admin Tools

Welcome to Admin Tools

On this page you will find admin functionality for the Q-Mintership App. Including the 'blockList' for blocking comments from certain names, and manual creation of invite transactions.

More features will be added as time goes on. This is the start of the functionality here.

` document.body.appendChild(mainContent) await addToolsPageEventListeners() } const addToolsPageEventListeners= async () => { document.getElementById("toggle-blocklist-button").addEventListener("click", async () => { const container = document.getElementById("blocklist-container") // toggle show/hide container.style.display = (container.style.display === "none" ? "flex" : "none") // if showing, load the block list if (container.style.display === "flex") { const currentBlockList = await fetchBlockList() displayBlockList(currentBlockList) } }) document.getElementById("blocklist-add-button").addEventListener("click", async () => { const blocklistInput = document.getElementById("blocklist-input") const nameToAdd = blocklistInput.value.trim() if (!nameToAdd) return // fetch existing const currentBlockList = await fetchBlockList() // add if not already in list if (!currentBlockList.includes(nameToAdd)) { currentBlockList.push(nameToAdd) } // publish updated await publishBlockList(currentBlockList) displayBlockList(currentBlockList) blocklistInput.value = "" alert(`"${nameToAdd}" added to the block list!`) }) // Remove document.getElementById("blocklist-remove-button").addEventListener("click", async () => { const blocklistInput = document.getElementById("blocklist-input") const nameToRemove = blocklistInput.value.trim() if (!nameToRemove) return // fetch existing let currentBlockList = await fetchBlockList() // remove if present currentBlockList = currentBlockList.filter(name => name !== nameToRemove) // publish updated await publishBlockList(currentBlockList) displayBlockList(currentBlockList) blocklistInput.value = "" alert(`"${nameToRemove}" removed from the block list (if it was present).`) }) document.getElementById("invite-user-button").addEventListener("click", async () => { const inviteInput = document.getElementById("invite-input") const nameOrAddress = inviteInput.value.trim() if (!nameOrAddress) return try { // We'll call some function handleManualInvite(nameOrAddress) await handleManualInvite(nameOrAddress) inviteInput.value = "" } catch (err) { console.error("Error inviting user:", err) alert("Failed to invite user.") } }) document.getElementById("create-group-invite").addEventListener("click", async () => { const inviteContainer = document.getElementById("invite-container") // Toggle display inviteContainer.style.display = (inviteContainer.style.display === "none" ? "flex" : "none") // If showing, load the pending invites if (inviteContainer.style.display === "flex") { const pendingInvites = await fetchPendingInvites() await displayPendingInviteDetails(pendingInvites) } }) } const displayBlockList = (blockedNames) => { const blocklistDisplay = document.getElementById("blocklist-display") if (!blockedNames || blockedNames.length === 0) { blocklistDisplay.innerHTML = "

No blocked users currently.

" return } blocklistDisplay.innerHTML = ` ` } const fetchPendingInvites = async () => { try { const { finalInviteTxs, pendingInviteTxs } = await fetchAllInviteTransactions() return pendingInviteTxs } catch (err) { console.error("Error fetching pending invites:", err) return [] } } const handleManualInvite = async (nameOrAddress) => { const addressInfo = await getAddressInfo(nameOrAddress) let address = addressInfo.address if (addressInfo && address) { console.log(`address is ${address}`) } else { // it might be a Qortal name => getNameInfo const nameData = await getNameInfo(nameOrAddress) if (!nameData || !nameData.owner) { throw new Error(`Cannot find valid address for ${nameOrAddress}`) } address = nameData.owner } const adminPublicKey = await getPublicKeyByName(userState.accountName) const timeToLive = 864000 // e.g. 10 days in seconds const fee = 0.01 let txGroupId = 694 // build the raw invite transaction const rawInviteTransaction = await createGroupInviteTransaction( address, adminPublicKey, 694, address, timeToLive, txGroupId, fee ) // sign const signedTransaction = await qortalRequest({ action: "SIGN_TRANSACTION", unsignedBytes: rawInviteTransaction }) if (!signedTransaction) { throw new Error("SIGN_TRANSACTION returned null. Possibly user canceled or an older UI?") } // process const processResponse = await processTransaction(signedTransaction) if (!processResponse) { throw new Error("Failed to process transaction. Possibly canceled or error from Qortal Core.") } alert(`Invite transaction submitted for ${nameOrAddress}. Wait for confirmation.`) } const displayPendingInviteDetails = async (pendingInvites) => { const invitesContainer = document.getElementById('pending-invites-display') if (!pendingInvites || pendingInvites.length === 0) { invitesContainer.innerHTML = "

No pending invites found.

" return } let html = `

Current Pending Invites:

` for (const inviteTx of pendingInvites) { const inviteeAddress = inviteTx.invitee const dateStr = new Date(inviteTx.timestamp).toLocaleString() let inviteeName = "" const txSig = inviteTx.signature const creatorName = await getNameFromAddress(inviteTx.creatorAddress) if (!creatorName) { creatorName = inviteTx.creatorAddress } try { // fetch the name from address, if it fails we keep it blank or fallback to the address inviteeName = await getNameFromAddress(inviteeAddress) if (!inviteeName || inviteeName === inviteeAddress) { inviteeName = inviteeAddress // fallback } } catch (err) { inviteeName = inviteeAddress // fallback if getName fails } const approvalSearchResults = await searchTransactions({ txTypes: ['GROUP_APPROVAL'], confirmationStatus: 'CONFIRMED', limit: 0, reverse: false, offset: 0, startBlock: 1990000, blockLimit: 0, txGroupId: 0 }) const approvals = approvalSearchResults.filter( (approvalTx) => approvalTx.pendingSignature === txSig ) const { tableHtml, approvalCount } = await buildApprovalTableHtml(approvals, getNameFromAddress) const finalTable = approvals.length > 0 ? tableHtml : "

No Approvals Found

" html += `
Invite Tx:

${inviteTx.signature.slice(0, 8)}...

Invitee:

${inviteeName}

Date:

${dateStr}

CreatorName:

${creatorName}

Total Approvals:

${approvalCount}

Existing Approvals: ${finalTable}
` } html += "
" invitesContainer.innerHTML = html }