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.
This commit is contained in:
parent
790182f585
commit
39f1d8783b
@ -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
|
||||
</button>
|
||||
<button onclick="handleBanMinter('${name}')"
|
||||
style="padding: 10px; background:rgb(93, 7, 7); color: white; border: none; cursor: pointer; border-radius: 5px;"
|
||||
onmouseover="this.style.backgroundColor='rgb(39, 9, 9) '"
|
||||
onmouseout="this.style.backgroundColor='rgb(93, 7, 7) '">
|
||||
BAN Minter
|
||||
Create BAN Tx
|
||||
</button>
|
||||
</div>
|
||||
`
|
||||
@ -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
|
||||
|
||||
|
@ -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 `
|
||||
<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; cursor: pointer; border-radius: 5px;"
|
||||
style="padding: 10px; background:rgb(0, 109, 76) ; color: white; border: dotted; border-color: white; cursor: pointer; border-radius: 5px;"
|
||||
onmouseover="this.style.backgroundColor='rgb(25, 47, 39) '"
|
||||
onmouseout="this.style.backgroundColor='rgba(7, 122, 101, 0.63) '"
|
||||
>
|
||||
Invite Minter
|
||||
Create Minter Invite
|
||||
</button>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
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 checkGroupApprovalAndCreateButton = async (address, cardIdentifier, transactionType) => {
|
||||
const txTypes = [`${transactionType}`]
|
||||
const confirmationStatus = 'CONFIRMED'
|
||||
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"])
|
||||
}
|
||||
|
||||
const groupApprovalSearchResults = await searchTransactions(txTypes, address, confirmationStatus, limit, reverse, offset)
|
||||
const pendingApprovals = groupApprovalSearchResults.filter(tx => tx.approvalStatus === 'PENDING')
|
||||
// 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 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 = `
|
||||
<div id="approval-button-container-${cardIdentifier}" style="margin-top: 1em;">
|
||||
<h2 style="color:rgb(181, 214, 100);">Existing Invite Approvals: ${existingApprovalCount}</h2>
|
||||
<button
|
||||
style="padding: 8px; background:rgb(37, 99, 44); color: #000; border: 1px solid #333; border-radius: 5px; cursor: pointer;"
|
||||
style="padding: 8px; background:rgb(37, 97, 99); color:rgb(215, 215, 215) ; border: 1px solid #333; border-color: white; border-radius: 5px; cursor: pointer;"
|
||||
onmouseover="this.style.backgroundColor='rgb(25, 47, 39) '"
|
||||
onmouseout="this.style.backgroundColor='rgb(37, 99, 44) '"
|
||||
onclick="handleGroupApproval('${address}','${txSig}')">
|
||||
Approve Invite
|
||||
onmouseout="this.style.backgroundColor='rgb(37, 96, 99) '"
|
||||
onclick="handleGroupApproval('${txSig}')">
|
||||
Approve Invite Tx
|
||||
</button>
|
||||
</div>
|
||||
`
|
||||
@ -1138,12 +1221,13 @@ const checkGroupApprovalAndCreateButton = async (address, cardIdentifier, transa
|
||||
|
||||
const approvalButtonHtml = `
|
||||
<div id="approval-button-container-${cardIdentifier}" style="margin-top: 1em;">
|
||||
<h2 style="color:rgb(199, 100, 64);">Existing Kick Approvals: ${existingApprovalCount}</h2>
|
||||
<button
|
||||
style="padding: 8px; background:rgb(119, 91, 21); color: #000; border: 1px solid #333; border-radius: 5px; cursor: pointer;"
|
||||
style="padding: 8px; background:rgb(119, 91, 21); color:rgb(201, 255, 251) ; border: 1px solid #333; border-color:rgb(102, 69, 60); border-radius: 5px; cursor: pointer;"
|
||||
onmouseover="this.style.backgroundColor='rgb(50, 52, 51) '"
|
||||
onmouseout="this.style.backgroundColor='rgb(119, 91, 21) '"
|
||||
onclick="handleGroupApproval('${address}','${txSig}')">
|
||||
Approve Kick
|
||||
onclick="handleGroupApproval('${txSig}')">
|
||||
Approve Kick Tx
|
||||
</button>
|
||||
</div>
|
||||
`
|
||||
@ -1154,12 +1238,13 @@ const checkGroupApprovalAndCreateButton = async (address, cardIdentifier, transa
|
||||
|
||||
const approvalButtonHtml = `
|
||||
<div id="approval-button-container-${cardIdentifier}" style="margin-top: 1em;">
|
||||
<h2 style="color:rgb(189, 40, 40);">Existing Ban Approvals: ${existingApprovalCount}</h2>
|
||||
<button
|
||||
style="padding: 8px; background:rgb(54, 7, 7); color: #000; border: 1px solid #333; border-radius: 5px; cursor: pointer;"
|
||||
style="padding: 8px; background:rgb(54, 7, 7); color:rgb(201, 255, 251) ; border: 1px solid #333; border-color:rgb(204, 94, 94); border-radius: 5px; cursor: pointer;"
|
||||
onmouseover="this.style.backgroundColor='rgb(50, 52, 51) '"
|
||||
onmouseout="this.style.backgroundColor='rgb(54, 7, 7) '"
|
||||
onclick="handleGroupApproval('${address}','${txSig}')">
|
||||
Approve Kick
|
||||
onclick="handleGroupApproval('${txSig}')">
|
||||
Approve Ban Tx
|
||||
</button>
|
||||
</div>
|
||||
`
|
||||
@ -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 = `
|
||||
<div id="join-button-container-${cardIdentifier}" style="margin-top: 1em;">
|
||||
<button
|
||||
style="padding: 8px; background:rgb(37, 99, 44); color: #000; border: 1px solid #333; border-radius: 5px; cursor: pointer;"
|
||||
style="padding: 8px; background:rgb(37, 99, 44); color:rgb(240, 240, 240); border: 1px solid rgb(255, 255, 255); border-radius: 5px; cursor: pointer;"
|
||||
onmouseover="this.style.backgroundColor='rgb(25, 47, 39) '"
|
||||
onmouseout="this.style.backgroundColor='rgb(37, 99, 44) '"
|
||||
onclick="handleJoinGroup('${userState.accountAddress}')">
|
||||
Approve Invite
|
||||
Join MINTER Group
|
||||
</button>
|
||||
</div>
|
||||
`
|
||||
}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 = ''
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
@ -1495,7 +1523,6 @@ const createGroupBanTransaction = async (recipientAddress, adminPublicKey, group
|
||||
reference: accountReference,
|
||||
fee,
|
||||
txGroupId,
|
||||
recipient: null,
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
28
index.html
28
index.html
@ -45,7 +45,7 @@
|
||||
</a>
|
||||
</span>
|
||||
<span class="navbar-caption-wrap">
|
||||
<a class="navbar-caption display-4" href="index.html">Q-Mintership Alpha
|
||||
<a class="navbar-caption display-4" href="index.html">Q-Mintership-Alpha (v0.84b)
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
@ -64,7 +64,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 Alpha v0.72b<br></a></span>
|
||||
<span class="navbar-caption-wrap"><a class="navbar-caption text-primary display-4" href="index.html">Q-Mintership Alpha v0.84b<br></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>
|
||||
|
||||
@ -169,6 +169,26 @@
|
||||
|
||||
<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">
|
||||
v0.84beta 01-13-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>NEW Features</u></b>- <b>All GROUP_APPROVAL and Transaction functionality</b> - 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 - <a href="MINTERSHIP-FORUM">FORUM</a> </p><p></p>
|
||||
<p class="mbr-text mbr-fonts-style display-7"><b><u>JOIN MINTER GROUP</u></b> - <b>Join the Minter Group from your Minter Card</b> - 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. </p><p></p>
|
||||
<p class="mbr-text mbr-fonts-style display-7"><b><u>CHECK THE ANNOUNCEMENTS</b></u> - <b>in the <a href="MINTERSHIP-FORUM">FORUM</a> </b> </p><p>on the 15th page of General room, and other related information in the MINTER ROOM on page 5.</p>
|
||||
<p class="mbr-text mbr-fonts-style display-7"><b>Various additional fixes and cleanup</b>. 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.</p><p>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'. </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-7 card">
|
||||
@ -458,12 +478,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 Alpha</h2>
|
||||
<h2 class="mbr-section-title mbr-fonts-style display-5">Q-Mintership-Alpha</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="link-wrap" href="#">
|
||||
<p class="mbr-link mbr-fonts-style display-4">Q-Mintership v0.72beta</p>
|
||||
<p class="mbr-link mbr-fonts-style display-4">Q-Mintership-Alpha v0.84beta</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
|
Loading…
Reference in New Issue
Block a user