From 39f1d8783bacf1dde88604e12cc2e8f6e90dc00f Mon Sep 17 00:00:00 2001 From: crowetic Date: Mon, 13 Jan 2025 15:53:20 -0800 Subject: [PATCH] Many new features to handle all GROUP_APPROVAL transactions, creation and approval. All tested on the DevNet in versiuons 0.74-0.83, as such this is 0.84. See details in the General Room of the forum on page 15. --- assets/js/AdminBoard.js | 43 ++++++--- assets/js/MinterBoard.js | 186 +++++++++++++++++++++++++----------- assets/js/QortalApi.js | 201 +++++++++++++++++++++++++-------------- index.html | 28 +++++- 4 files changed, 316 insertions(+), 142 deletions(-) diff --git a/assets/js/AdminBoard.js b/assets/js/AdminBoard.js index 1794314..4dc000d 100644 --- a/assets/js/AdminBoard.js +++ b/assets/js/AdminBoard.js @@ -445,7 +445,13 @@ const validateMinterName = async(minterName) => { try { const nameInfo = await getNameInfo(minterName) const name = nameInfo.name - return name + if (name) { + console.log(`name information found, returning:`, name) + return name + } else { + console.warn(`no name information found, this is not a registered name: '${minterName}', Returning null`, name) + return null + } } catch (error){ console.error(`extracting name from name info: ${minterName} failed.`, error) return null @@ -831,13 +837,13 @@ const createRemoveButtonHtml = (name, cardIdentifier) => { style="padding: 10px; background: rgb(134, 80, 4); color: white; border: none; cursor: pointer; border-radius: 5px;" onmouseover="this.style.backgroundColor='rgb(47, 28, 11) '" onmouseout="this.style.backgroundColor='rgb(134, 80, 4) '"> - KICK Minter + Create KICK Tx ` @@ -847,18 +853,18 @@ const handleKickMinter = async (minterName) => { try { // Optional block check let txGroupId = 0 - const { height: currentHeight } = await getLatestBlockInfo() - const isBlockPassed = currentHeight >= GROUP_APPROVAL_FEATURE_TRIGGER_HEIGHT + // const { height: currentHeight } = await getLatestBlockInfo() + const isBlockPassed = await featureTriggerCheck() if (isBlockPassed) { console.log(`block height above featureTrigger Height, using group approval method...txGroupId 694`) txGroupId = 694 } // Get the minter address from name info - const minterInfo = await getNameInfo(minterName) - const minterAddress = minterInfo?.owner + const minterNameInfo = await getNameInfo(minterName) + const minterAddress = minterNameInfo?.owner if (!minterAddress) { - alert(`No valid address found for minter name: ${minterName}`) + alert(`No valid address found for minter name: ${minterName}, this should NOT have happened, please report to developers...`) return } @@ -872,6 +878,11 @@ const handleKickMinter = async (minterName) => { action: "SIGN_TRANSACTION", unsignedBytes: rawKickTransaction }) + if (!signedKickTransaction) { + console.warn(`this only happens if the SIGN_TRANSACTION qortalRequest failed... are you using the legacy UI prior to this qortalRequest being added?`) + alert(`this only happens if the SIGN_TRANSACTION qortalRequest failed... are you using the legacy UI prior to this qortalRequest being added? Please talk to developers.`) + return + } let txToProcess = signedKickTransaction @@ -894,8 +905,9 @@ const handleKickMinter = async (minterName) => { const handleBanMinter = async (minterName) => { try { let txGroupId = 0 - const { height: currentHeight } = await getLatestBlockInfo() - if (currentHeight <= GROUP_APPROVAL_FEATURE_TRIGGER_HEIGHT) { + // const { height: currentHeight } = await getLatestBlockInfo() + const isBlockPassed = await featureTriggerCheck() + if (!isBlockPassed) { console.log(`block height is under the removal featureTrigger height, using txGroupId 0`) txGroupId = 0 } else { @@ -903,11 +915,11 @@ const handleBanMinter = async (minterName) => { txGroupId = 694 } - const minterInfo = await getNameInfo(minterName) - const minterAddress = minterInfo?.owner + const minterNameInfo = await getNameInfo(minterName) + const minterAddress = minterNameInfo?.owner if (!minterAddress) { - alert(`No valid address found for minter name: ${minterName}`) + alert(`No valid address found for minter name: ${minterName}, this should NOT have happened, please report to developers...`) return } @@ -921,6 +933,11 @@ const handleBanMinter = async (minterName) => { action: "SIGN_TRANSACTION", unsignedBytes: rawBanTransaction }) + if (!signedBanTransaction) { + console.warn(`this only happens if the SIGN_TRANSACTION qortalRequest failed... are you using the legacy UI prior to this qortalRequest being added?`) + alert(`this only happens if the SIGN_TRANSACTION qortalRequest failed... are you using the legacy UI prior to this qortalRequest being added? Please talk to developers.`) + return + } let txToProcess = signedBanTransaction diff --git a/assets/js/MinterBoard.js b/assets/js/MinterBoard.js index 4756371..78285b6 100644 --- a/assets/js/MinterBoard.js +++ b/assets/js/MinterBoard.js @@ -5,7 +5,8 @@ let isExistingCard = false let existingCardData = {} let existingCardIdentifier = {} const MIN_ADMIN_YES_VOTES = 9; -const GROUP_APPROVAL_FEATURE_TRIGGER_HEIGHT = 99999999 //TODO update this to correct featureTrigger height when known, either that, or pull from core. +const GROUP_APPROVAL_FEATURE_TRIGGER_HEIGHT = 9999950 //TODO update this to correct featureTrigger height when known, either that, or pull from core. +let featureTriggerPassed = false let isApproved = false const loadMinterBoardPage = async () => { @@ -117,7 +118,7 @@ const loadMinterBoardPage = async () => { event.preventDefault() await publishCard() }) - + await featureTriggerCheck() await loadCards() } @@ -514,8 +515,15 @@ const processPollData= async (pollData, minterGroupMembers, minterAdmins, creato const memberAddresses = minterGroupMembers.map(m => m.member) const minterAdminAddresses = minterAdmins.map(m => m.member) const adminGroupsMembers = await fetchAllAdminGroupsMembers() + const featureTriggerPassed = await featureTriggerCheck() const groupAdminAddresses = adminGroupsMembers.map(m => m.member) - const adminAddresses = [...minterAdminAddresses, ...groupAdminAddresses] + let adminAddresses = [...minterAdminAddresses] + + if (!featureTriggerPassed) { + console.log(`featureTrigger is NOT passed, only showing admin results from Minter Admins and Group Admins`) + adminAddresses = [...minterAdminAddresses, ...groupAdminAddresses] + } + let adminYes = 0, adminNo = 0 let minterYes = 0, minterNo = 0 let yesWeight = 0, noWeight = 0 @@ -1050,19 +1058,32 @@ const createInviteButtonHtml = (creator, cardIdentifier) => { return `
` } -const checkAndDisplayInviteButton = async (adminYes, creator, cardIdentifier) => { +const featureTriggerCheck = async () => { const latestBlockInfo = await getLatestBlockInfo() const isBlockPassed = latestBlockInfo.height >= GROUP_APPROVAL_FEATURE_TRIGGER_HEIGHT + if (isBlockPassed) { + console.warn(`featureTrigger check (verifyFeatureTrigger) determined block has PASSED:`, isBlockPassed) + featureTriggerPassed = true + return true + } else { + console.warn(`featureTrigger check (verifyFeatureTrigger) determined block has NOT PASSED:`, isBlockPassed) + featureTriggerPassed = false + return false + } +} + +const checkAndDisplayInviteButton = async (adminYes, creator, cardIdentifier) => { + const isBlockPassed = await featureTriggerCheck() let minAdminCount const minterAdmins = await fetchMinterGroupAdmins() @@ -1081,10 +1102,10 @@ const checkAndDisplayInviteButton = async (adminYes, creator, cardIdentifier) => } if (isBlockPassed) { - const minterAddressInfo = await getNameInfo(creator) - const minterAddress = await minterAddressInfo.owner + const minterNameInfo = await getNameInfo(creator) + const minterAddress = await minterNameInfo.owner if (userState.isMinterAdmin){ - let groupApprovalHtml = await checkGroupApprovalAndCreateButton(minterAddress, cardIdentifier, 'GROUP_INVITE') + let groupApprovalHtml = await checkGroupApprovalAndCreateButton(minterAddress, cardIdentifier, "GROUP_INVITE") if (groupApprovalHtml) { return groupApprovalHtml } @@ -1093,41 +1114,103 @@ const checkAndDisplayInviteButton = async (adminYes, creator, cardIdentifier) => } } - if (adminYes >= minAdminCount && userState.isMinterAdmin) { - const inviteButtonHtml = createInviteButtonHtml(creator, cardIdentifier) - console.log(`admin votes over 9, creating invite button...`, adminYes) + if (adminYes >= minAdminCount && (userState.isMinterAdmin)) { + const inviteButtonHtml = createInviteButtonHtml(creator, cardIdentifier) + console.log(`admin votes over 9, creating invite button...`, adminYes) return inviteButtonHtml } return null } +const findPendingApprovalTxForAddress = async (address, txType, limit = 0, offset = 0) => { + // 1) Fetch all pending transactions + const pendingTxs = await searchPendingTransactions(limit, offset) + // if a txType is passed, return the results related to that type, if not, then return any pending tx of the potential types. + let relevantTypes + if (txType) { + relevantTypes = new Set([txType]) + } else { + relevantTypes = new Set(["GROUP_INVITE", "GROUP_BAN", "GROUP_KICK"]) + } + + // Filter pending TX for relevant types + const relevantTxs = pendingTxs.filter((tx) => relevantTypes.has(tx.type)) + + // Further filter by whether 'address' matches the correct field + // - GROUP_INVITE => invitee + // - GROUP_BAN => offender + // - GROUP_KICK => member + // If the user passed a specific txType, only one branch might matter. + const matchedTxs = relevantTxs.filter((tx) => { + switch (tx.type) { + case "GROUP_INVITE": + return tx.invitee === address + case "GROUP_BAN": + return tx.offender === address + case "GROUP_KICK": + return tx.member === address + default: + return false + } + }) + + return matchedTxs // Array of matching pending transactions +} + const checkGroupApprovalAndCreateButton = async (address, cardIdentifier, transactionType) => { - const txTypes = [`${transactionType}`] - const confirmationStatus = 'CONFIRMED' + const txTypes = [transactionType] - const groupApprovalSearchResults = await searchTransactions(txTypes, address, confirmationStatus, limit, reverse, offset) - const pendingApprovals = groupApprovalSearchResults.filter(tx => tx.approvalStatus === 'PENDING') + const txSearchResults = await searchTransactions({ + txTypes, + address: `${address}`, + confirmationStatus: 'CONFIRMED', + limit: 0, + reverse: true, + offset: 0, + startBlock: 1990000, + blockLimit: 0, + txGroupId: 694 + }) + const approvalTxType = ['GROUP_APPROVAL'] + const approvalSearchResults = await searchTransactions({ + txTypes: approvalTxType, + address: `${address}`, + confirmationStatus: 'CONFIRMED', + limit: 0, + reverse: true, + offset: 0, + startBlock: 1990000, + blockLimit: 0, + txGroupId: 0 + }) + + console.warn(`transaction search results, this is for comparison to pendingApprovals search, these are not used:`,txSearchResults) + const pendingApprovals = await findPendingApprovalTxForAddress(address, transactionType) + if (pendingApprovals) { - console.warn(`pendingApprovals FOUND: ${pendingApprovals}`) + console.warn(`this is what is used for pending results... pendingApprovals FOUND:`, pendingApprovals) } - if (pendingApprovals.length === 0) { - return + if ((pendingApprovals.length === 0) || (!pendingApprovals)) { + console.warn(`no pending approval transactions found, returning null...`) + return null } + const existingApprovalCount = approvalSearchResults.length const txSig = pendingApprovals[0].signature if (transactionType === `GROUP_INVITE`){ const approvalButtonHtml = `
+

Existing Invite Approvals: ${existingApprovalCount}

` @@ -1138,12 +1221,13 @@ const checkGroupApprovalAndCreateButton = async (address, cardIdentifier, transa const approvalButtonHtml = `
+

Existing Kick Approvals: ${existingApprovalCount}

` @@ -1154,12 +1238,13 @@ const checkGroupApprovalAndCreateButton = async (address, cardIdentifier, transa const approvalButtonHtml = `
+

Existing Ban Approvals: ${existingApprovalCount}

` @@ -1167,7 +1252,7 @@ const checkGroupApprovalAndCreateButton = async (address, cardIdentifier, transa } } -const handleGroupApproval = async (address, pendingApprovalSignature) => { +const handleGroupApproval = async (pendingSignature) => { try{ if (!userState.isMinterAdmin) { console.warn(`non-admin attempting to sign approval!`) @@ -1175,22 +1260,14 @@ const handleGroupApproval = async (address, pendingApprovalSignature) => { } const fee = 0.01 const adminPublicKey = await getPublicKeyByName(userState.accountName) - const txGroupId = 694 - const rawGroupApprovalTransaction = await createGroupApprovalTransaction(address, adminPublicKey, pendingApprovalSignature, txGroupId, fee) + const txGroupId = 0 + const rawGroupApprovalTransaction = await createGroupApprovalTransaction(adminPublicKey, pendingSignature, txGroupId, fee) const signedGroupApprovalTransaction = await qortalRequest({ action: "SIGN_TRANSACTION", unsignedBytes: rawGroupApprovalTransaction }) - // const switchToBase58 = isBase64(signedGroupApprovalTransaction) - let txToProcess = signedGroupApprovalTransaction - // if (switchToBase58){ - // console.warn(`base64 tx found, converting to base58`,signedGroupApprovalTransaction) - // const convertedToHex = await base64ToHex(signedGroupApprovalTransaction) - // const base58TxData = await hexToBase58(convertedToHex) - // txToProcess = base58TxData - // console.log(`base58ConvertedSignedTxData to process:`,txToProcess) - // } + let txToProcess = signedGroupApprovalTransaction const processGroupApprovalTx = await processTransaction(txToProcess) if (processGroupApprovalTx) { @@ -1209,23 +1286,24 @@ const handleJoinGroup = async (minterAddress) => { try{ if (userState.accountAddress === minterAddress) { console.log(`minter user found `) + + const qRequestAttempt = await qortalRequest({ + action: "JOIN_GROUP", + groupId: 694 + }) + + if (qRequestAttempt) { + return true + } + const joinerPublicKey = getPublicKeyFromAddress(minterAddress) - fee = 0.01 + const fee = 0.01 const joinGroupTransactionData = await createGroupJoinTransaction(minterAddress, joinerPublicKey, 694, 0, fee) const signedJoinGroupTransaction = await qortalRequest({ action: "SIGN_TRANSACTION", unsignedBytes: joinGroupTransactionData }) let txToProcess = signedJoinGroupTransaction - // const switchToBase58 = isBase64(signedJoinGroupTransaction) - - // if (switchToBase58){ - // console.warn(`base64 tx found, converting to base58`, signedJoinGroupTransaction) - // const convertedToHex = await base64ToHex(signedJoinGroupTransaction) - // const base58TxData = await hexToBase58(convertedToHex) - // txToProcess = base58TxData - // console.log(`base58ConvertedSignedTxData to process:`,txToProcess) - // } const processJoinGroupTransaction = await processTransaction(txToProcess) if (processJoinGroupTransaction){ @@ -1295,16 +1373,16 @@ const createCardHTML = async (cardData, pollResults, cardIdentifier, commentCoun inviteHtmlAdd = `
` }else{ - console.log(`user is not the minter... displaying no join button`) + console.log(`user is not the minter... NOT displaying any join button`) inviteHtmlAdd = '' } } diff --git a/assets/js/QortalApi.js b/assets/js/QortalApi.js index da8be18..0c9ac77 100644 --- a/assets/js/QortalApi.js +++ b/assets/js/QortalApi.js @@ -267,35 +267,35 @@ const verifyUserIsAdmin = async () => { console.log('userGroups:', userGroups) const minterGroupAdmins = await fetchMinterGroupAdmins() - console.log('minterGroupAdmins.members:', minterGroupAdmins) + console.log('minterGroupAdmins:', minterGroupAdmins) if (!Array.isArray(userGroups)) { throw new Error('userGroups is not an array or is undefined') } if (!Array.isArray(minterGroupAdmins)) { - throw new Error('minterGroupAdmins.members is not an array or is undefined') + throw new Error('minterGroupAdmins is not an array or is undefined') } const isAdmin = userGroups.some(group => adminGroups.includes(group.groupName)) const isMinterAdmin = minterGroupAdmins.some(admin => admin.member === userState.accountAddress && admin.isAdmin) - if (isMinterAdmin) { - userState.isMinterAdmin = true + userState.isMinterAdmin = isMinterAdmin + userState.isAdmin = isMinterAdmin || isAdmin + userState.isForumAdmin = isAdmin + + if ((userState.isAdmin) || (userState.isMinterAdmin || userState.isForumAdmin)){ + console.log(`user is one of the following: admin: ${userState.isAdmin} - minterAdmin: ${userState.isMinterAdmin} - forumAdmin: ${userState.isForumAdmin}`) + return userState.isAdmin + } else { + return false } - if (isAdmin) { - userState.isAdmin = true - userState.isForumAdmin = true - } - return userState.isAdmin } catch (error) { console.error('Error verifying user admin status:', error) throw error } } - - const verifyAddressIsAdmin = async (address) => { console.log('verifyAddressIsAdmin called') console.log('address:', address) @@ -307,7 +307,7 @@ const verifyAddressIsAdmin = async (address) => { const userGroups = await getUserGroups(address) const minterGroupAdmins = await fetchMinterGroupAdmins() const isAdmin = await userGroups.some(group => adminGroups.includes(group.groupName)) - const isMinterAdmin = minterGroupAdmins.members.some(admin => admin.member === address && admin.isAdmin) + const isMinterAdmin = minterGroupAdmins.some(admin => admin.member === address && admin.isAdmin) if ((isMinterAdmin) || (isAdmin)) { return true } else { @@ -483,27 +483,61 @@ const fetchMinterGroupAdmins = async () => { //use what is returned .member to obtain each member... {"member": "memberAddress", "isAdmin": "true"} } -const fetchAllAdminGroupsMembers = async () => { - try { - let adminGroupMemberAddresses = [] // Declare outside loop to accumulate results - for (const groupID of adminGroupIDs) { - const response = await fetch(`${baseUrl}/groups/members/${groupID}?limit=0`, { - method: 'GET', - headers: { 'Accept': 'application/json' }, - }) +// const fetchAllAdminGroupsMembers = async () => { +// try { +// let adminGroupMemberAddresses = [] // Declare outside loop to accumulate results +// for (const groupID of adminGroupIDs) { +// const response = await fetch(`${baseUrl}/groups/members/${groupID}?limit=0`, { +// method: 'GET', +// headers: { 'Accept': 'application/json' }, +// }) - const groupData = await response.json() - if (groupData.members && Array.isArray(groupData.members)) { - adminGroupMemberAddresses.push(...groupData.members) // Merge members into the array - } else { - console.warn(`Group ${groupID} did not return valid members.`) +// const groupData = await response.json() +// if (groupData.members && Array.isArray(groupData.members)) { +// adminGroupMemberAddresses.push(...groupData.members) // Merge members into the array +// } else { +// console.warn(`Group ${groupID} did not return valid members.`) +// } +// } +// return adminGroupMemberAddresses +// } catch (error) { +// console.log('Error fetching admin group members', error) +// } +// } + +const fetchAllAdminGroupsMembers = async () => { + try { + // We'll track addresses so we don't duplicate the same .member + const seenAddresses = new Set() + const resultObjects = [] + + for (const groupID of adminGroupIDs) { + const response = await fetch(`${baseUrl}/groups/members/${groupID}?limit=0`, { + method: 'GET', + headers: { Accept: 'application/json' }, + }) + + const groupData = await response.json() + if (Array.isArray(groupData?.members)) { + for (const memberObj of groupData.members) { + if (memberObj?.member && !seenAddresses.has(memberObj.member)) { + // Add to final results + resultObjects.push(memberObj) + // Mark address as seen + seenAddresses.add(memberObj.member) } + } + } else { + console.warn(`Group ${groupID} did not return valid members.`) } - return adminGroupMemberAddresses + } + + return resultObjects // array of objects e.g. [{member, joined}, ...] } catch (error) { - console.log('Error fetching admin group members', error) + console.error('Error fetching admin group members', error) + return [] } -} + } const fetchMinterGroupMembers = async () => { try { @@ -548,27 +582,21 @@ const fetchAllGroups = async (limit) => { const fetchAdminGroupsMembersPublicKeys = async () => { try { - let adminGroupMemberAddresses = [] // Declare outside loop to accumulate results - for (const groupID of adminGroupIDs) { - const response = await fetch(`${baseUrl}/groups/members/${groupID}?limit=0`, { - method: 'GET', - headers: { 'Accept': 'application/json' }, - }) - const groupData = await response.json() - if (groupData.members && Array.isArray(groupData.members)) { - adminGroupMemberAddresses.push(...groupData.members) // Merge members into the array - } else { - console.warn(`Group ${groupID} did not return valid members.`) - } - } + let adminGroupMemberAddresses = await fetchAllAdminGroupsMembers() + let minterAdminMemberAddresses = await fetchMinterGroupAdmins() - // Check if adminGroupMemberAddresses has valid data if (!Array.isArray(adminGroupMemberAddresses)) { throw new Error("Expected 'adminGroupMemberAddresses' to be an array but got a different structure") } - let allMemberPublicKeys = [] // Declare outside loop to accumulate results + if (Array.isArray(adminGroupMemberAddresses)) { + console.log(`adding + minterAdminMemberAddresses:`, minterAdminMemberAddresses) + adminGroupMemberAddresses.push(...minterAdminMemberAddresses) + console.log(`final = all adminGroupMemberAddresses`, adminGroupMemberAddresses) + } + + let allMemberPublicKeys = [] for (const member of adminGroupMemberAddresses) { const memberPublicKey = await getPublicKeyFromAddress(member.member) allMemberPublicKeys.push(memberPublicKey) @@ -1430,15 +1458,16 @@ const createGroupKickTransaction = async (recipientAddress, adminPublicKey, grou } } -const createGroupApprovalTransaction = async (recipientAddress, adminPublicKey, pendingApprovalSignature, txGroupId=694, fee=0.01) => { +const createGroupApprovalTransaction = async (adminPublicKey, pendingSignature, txGroupId=0, fee=0.01) => { try { // Fetch account reference correctly - const accountInfo = await getAddressInfo(recipientAddress) - const accountReference = accountInfo.reference + const adminAddress = await getAddressFromPublicKey(adminPublicKey) + const addressInfo = await getAddressInfo(adminAddress) + const accountReference = addressInfo.reference // Validate inputs before making the request - if (!adminPublicKey || !accountReference || !recipientAddress) { + if (!adminPublicKey || !accountReference ) { throw new Error("Missing required parameters for group invite transaction.") } @@ -1447,9 +1476,8 @@ const createGroupApprovalTransaction = async (recipientAddress, adminPublicKey, reference: accountReference, fee, txGroupId, - recipient: null, adminPublicKey, - pendingApprovalSignature, + pendingSignature, approval: true } @@ -1494,8 +1522,7 @@ const createGroupBanTransaction = async (recipientAddress, adminPublicKey, group timestamp: Date.now(), reference: accountReference, fee, - txGroupId, - recipient: null, + txGroupId, adminPublicKey, groupId, offender, @@ -1535,18 +1562,17 @@ const createGroupJoinTransaction = async (recipientAddress, joinerPublicKey, gro const accountReference = accountInfo.reference // Validate inputs before making the request - if (!adminPublicKey || !accountReference || !recipientAddress) { + if (!accountReference || !recipientAddress) { throw new Error("Missing required parameters for group invite transaction.") } const payload = { timestamp: Date.now(), reference: accountReference, - fee: fee | 0.01, - txGroupId: txGroupId, - recipient: null, + fee: fee, + txGroupId, joinerPublicKey, - groupId: groupId, + groupId } console.log("Sending GROUP_JOIN transaction payload:", payload) @@ -1639,35 +1665,35 @@ const searchTransactions = async ({ } = {}) => { try { // 1) Build the query string - const queryParams = []; + const queryParams = [] // Add each txType as multiple "txType=..." params txTypes.forEach(type => { - queryParams.push(`txType=${encodeURIComponent(type)}`); - }); + queryParams.push(`txType=${encodeURIComponent(type)}`) + }) // If startBlock is nonzero, push "startBlock=..." if (startBlock) { - queryParams.push(`startBlock=${encodeURIComponent(startBlock)}`); + queryParams.push(`startBlock=${encodeURIComponent(startBlock)}`) } // If blockLimit is nonzero, push "blockLimit=..." if (blockLimit) { - queryParams.push(`blockLimit=${encodeURIComponent(blockLimit)}`); + queryParams.push(`blockLimit=${encodeURIComponent(blockLimit)}`) } // If txGroupId is nonzero, push "txGroupId=..." if (txGroupId) { - queryParams.push(`txGroupId=${encodeURIComponent(txGroupId)}`); + queryParams.push(`txGroupId=${encodeURIComponent(txGroupId)}`) } // Address if (address) { - queryParams.push(`address=${encodeURIComponent(address)}`); + queryParams.push(`address=${encodeURIComponent(address)}`) } // Confirmation status if (confirmationStatus) { - queryParams.push(`confirmationStatus=${encodeURIComponent(confirmationStatus)}`); + queryParams.push(`confirmationStatus=${encodeURIComponent(confirmationStatus)}`) } // Limit (if you want to explicitly pass limit=0, consider whether to skip or not) if (limit !== undefined) { @@ -1684,6 +1710,7 @@ const searchTransactions = async ({ const queryString = queryParams.join('&'); const url = `${baseUrl}/transactions/search?${queryString}`; + console.warn(`calling the following for search transactions: ${url}`) // 2) Fetch const response = await fetch(url, { @@ -1691,15 +1718,15 @@ const searchTransactions = async ({ headers: { 'Accept': '*/*' } - }); + }) if (!response.ok) { - const errorText = await response.text(); - throw new Error(`Failed to search transactions: HTTP ${response.status}, ${errorText}`); + const errorText = await response.text() + throw new Error(`Failed to search transactions: HTTP ${response.status}, ${errorText}`) } // 3) Parse JSON - const txArray = await response.json(); + const txArray = await response.json() // Check if the response is indeed an array of transactions if (!Array.isArray(txArray)) { @@ -1708,10 +1735,42 @@ const searchTransactions = async ({ return txArray; // e.g. [{ type, timestamp, reference, ... }, ...] } catch (error) { - console.error("Error in searchTransactions:", error); - throw error; + console.error("Error in searchTransactions:", error) + throw error } - }; + } + +const searchPendingTransactions = async (limit = 20, offset = 0) => { + try { + const queryParams = [] + if (limit) queryParams.push(`limit=${limit}`) + if (offset) queryParams.push(`offset=${offset}`) + + const queryString = queryParams.join('&'); + const url = `${baseUrl}/transactions/pending${queryString ? `?${queryString}` : ''}` + + const response = await fetch(url, { + method: 'GET', + headers: { 'Accept': '*/*' }, + }) + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`Failed to search pending transactions: HTTP ${response.status}, ${errorText}`) + } + + const result = await response.json(); + if (!Array.isArray(result)) { + throw new Error("Expected an array for pending transactions, but got something else.") + } + + return result; // e.g. [{type, signature, approvalStatus, ...}, ...] + } catch (error) { + console.error("Error in searchPendingTransactions:", error) + throw error + } + } + diff --git a/index.html b/index.html index 40a638c..138c604 100644 --- a/index.html +++ b/index.html @@ -45,7 +45,7 @@ - Q-Mintership Alpha + Q-Mintership-Alpha (v0.84b) @@ -64,7 +64,7 @@ - Q-Mintership Alpha v0.72b
+ Q-Mintership Alpha v0.84b
@@ -169,6 +169,26 @@
+
+
+
+
+

+ v0.84beta 01-13-2025

+
+
+
+
+

+ NEW Features- All GROUP_APPROVAL and Transaction functionality - Ability to CREATE INVITE, KICK, AND BAN transactions, and functionality to allow GROUP_APPROVAL of said transactions. Checks for the featureTrigger and whether it has passed or not, to change the functionality to the new methods post-core-update. See page 15 of the General Room for details - FORUM

+

JOIN MINTER GROUP - Join the Minter Group from your Minter Card - After the minter who published a card is APPROVED (with GROUP_APPROVAL after next core update, or by an invite from crowetic prior, after 40%+ approval by Minter Admins...) a 'JOIN MINTER GROUP' button will appear on the card for the user that published it. Upon clicking this button a JOIN_GROUP transaction will be created, and processed, thus accepting the invite to the MINTER group.

+

CHECK THE ANNOUNCEMENTS - in the FORUM

on the 15th page of General room, and other related information in the MINTER ROOM on page 5.

+

Various additional fixes and cleanup. All of the above functionality has been TESTED on the DevNet, that is why there is a big jump in VERSION from the last to this one.

Many additional features are coming soon, including a NOTIFICATION SYSTEM, PROFILES AND EXPLORER, and MUCH MORE. Q-Mintership will become more than simply 'the app used to become a minter'.

+
+
+
+
+
@@ -458,12 +478,12 @@
-

Q-Mintership Alpha

+

Q-Mintership-Alpha

- +
-- 2.43.0