2024-12-12 01:45:27 +00:00
// const cardIdentifierPrefix = "test-board-card"
2024-12-17 03:53:37 +00:00
const testMode = true ;
2024-12-12 01:45:27 +00:00
const cardIdentifierPrefix = "testMB-board-card" ;
2024-12-11 22:40:32 +00:00
let isExistingCard = false ;
let existingCardData = { } ;
2024-12-17 03:53:37 +00:00
let existingCardIdentifier = { } ;
2024-12-11 22:40:32 +00:00
document . addEventListener ( "DOMContentLoaded" , async ( ) => {
const minterBoardLinks = document . querySelectorAll ( 'a[href="MINTER-BOARD"], a[href="MINTERS"]' ) ;
minterBoardLinks . forEach ( link => {
link . addEventListener ( "click" , async ( event ) => {
event . preventDefault ( ) ;
if ( ! userState . isLoggedIn ) {
await login ( ) ;
}
await loadMinterBoardPage ( ) ;
} ) ;
} ) ;
} ) ;
2024-12-13 01:23:34 +00:00
const loadMinterBoardPage = async ( ) => {
2024-12-11 22:40:32 +00:00
// Clear existing content on the page
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 ( ) ;
}
}
// Add the "Minter Board" content
const mainContent = document . createElement ( "div" ) ;
mainContent . innerHTML = `
< div class = "minter-board-main" style = "padding: 20px; text-align: center;" >
< h1 style = "color: lightblue;" > Minter Board < / h 1 >
2024-12-12 02:57:50 +00:00
< p style = "font-size: 1.25em;" > The Minter Board is a place to publish information about yourself in order to obtain support from existing Minters and Minter Admins on the Qortal network . You may publish a header , content , and links to other QDN - published content in order to support you in your mission . Minter Admins and Existing Minters will then support you ( or not ) by way of a vote on your card . Card details you publish , along with existing poll results , and comments from others , will be displayed here . Good Luck on your Qortal journey to becoming a minter ! < / p >
2024-12-11 22:40:32 +00:00
< button id = "publish-card-button" class = "publish-card-button" style = "margin: 20px; padding: 10px;" > Publish Minter Card < / b u t t o n >
2024-12-17 03:53:37 +00:00
< button id = "refresh-cards-button" class = "refresh-cards-button" style = "padding: 10px;" > Refresh Cards < / b u t t o n >
2024-12-11 22:40:32 +00:00
< div id = "cards-container" class = "cards-container" style = "margin-top: 20px;" > < / d i v >
< div id = "publish-card-view" class = "publish-card-view" style = "display: none; text-align: left; padding: 20px;" >
< form id = "publish-card-form" >
< h3 > Create or Update Your Minter Card < / h 3 >
< label for = "card-header" > Header : < / l a b e l >
< input type = "text" id = "card-header" maxlength = "100" placeholder = "Enter card header" required >
< label for = "card-content" > Content : < / l a b e l >
< textarea id = "card-content" placeholder = "Enter detailed information about why you deserve to be a minter..." required > < / t e x t a r e a >
< label for = "card-links" > Links ( qortal : //...):</label>
< div id = "links-container" >
< input type = "text" class = "card-link" placeholder = "Enter QDN link" >
< / d i v >
< button type = "button" id = "add-link-button" > Add Another Link < / b u t t o n >
< button type = "submit" id = "submit-publish-button" > Publish Card < / b u t t o n >
< button type = "button" id = "cancel-publish-button" > Cancel < / b u t t o n >
< / f o r m >
< / d i v >
< / d i v >
` ;
document . body . appendChild ( mainContent ) ;
document . getElementById ( "publish-card-button" ) . addEventListener ( "click" , async ( ) => {
try {
2024-12-17 03:53:37 +00:00
const fetchedCard = await fetchExistingCard ( ) ;
if ( fetchedCard ) {
// An existing card is found
if ( testMode ) {
// In test mode, ask user what to do
const updateCard = confirm ( "A card already exists. Do you want to update it?" ) ;
if ( updateCard ) {
isExistingCard = true ;
await loadCardIntoForm ( existingCardData ) ;
alert ( "Edit your existing card and publish." ) ;
} else {
alert ( "Test mode: You can now create a new card." ) ;
isExistingCard = false ;
existingCardData = { } ; // Reset
document . getElementById ( "publish-card-form" ) . reset ( ) ;
}
2024-12-11 22:40:32 +00:00
} else {
2024-12-17 03:53:37 +00:00
// Not in test mode, force editing
alert ( "A card already exists. Publishing of multiple cards is not allowed. Please update your card." ) ;
isExistingCard = true ;
await loadCardIntoForm ( existingCardData ) ;
2024-12-11 22:40:32 +00:00
}
} else {
2024-12-17 03:53:37 +00:00
// No existing card found
2024-12-11 22:40:32 +00:00
alert ( "No existing card found. Create a new card." ) ;
isExistingCard = false ;
}
2024-12-17 03:53:37 +00:00
// Show the form
2024-12-11 22:40:32 +00:00
const publishCardView = document . getElementById ( "publish-card-view" ) ;
publishCardView . style . display = "flex" ;
document . getElementById ( "cards-container" ) . style . display = "none" ;
} catch ( error ) {
console . error ( "Error checking for existing card:" , error ) ;
alert ( "Failed to check for existing card. Please try again." ) ;
}
} ) ;
2024-12-17 03:53:37 +00:00
document . getElementById ( "refresh-cards-button" ) . addEventListener ( "click" , async ( ) => {
const cardsContainer = document . getElementById ( "cards-container" ) ;
cardsContainer . innerHTML = "<p>Refreshing cards...</p>" ;
await loadCards ( ) ;
} ) ;
2024-12-13 01:23:34 +00:00
document . getElementById ( "cancel-publish-button" ) . addEventListener ( "click" , async ( ) => {
2024-12-11 22:40:32 +00:00
const cardsContainer = document . getElementById ( "cards-container" ) ;
cardsContainer . style . display = "flex" ; // Restore visibility
const publishCardView = document . getElementById ( "publish-card-view" ) ;
publishCardView . style . display = "none" ; // Hide the publish form
} ) ;
2024-12-13 01:23:34 +00:00
document . getElementById ( "add-link-button" ) . addEventListener ( "click" , async ( ) => {
2024-12-11 22:40:32 +00:00
const linksContainer = document . getElementById ( "links-container" ) ;
const newLinkInput = document . createElement ( "input" ) ;
newLinkInput . type = "text" ;
newLinkInput . className = "card-link" ;
newLinkInput . placeholder = "Enter QDN link" ;
linksContainer . appendChild ( newLinkInput ) ;
} ) ;
document . getElementById ( "publish-card-form" ) . addEventListener ( "submit" , async ( event ) => {
event . preventDefault ( ) ;
await publishCard ( ) ;
} ) ;
await loadCards ( ) ;
}
2024-12-13 01:23:34 +00:00
//Main function to load the Minter Cards ----------------------------------------
const loadCards = async ( ) => {
const cardsContainer = document . getElementById ( "cards-container" ) ;
cardsContainer . innerHTML = "<p>Loading cards...</p>" ;
try {
const response = await qortalRequest ( {
action : "SEARCH_QDN_RESOURCES" ,
service : "BLOG_POST" ,
query : cardIdentifierPrefix ,
mode : "ALL"
} ) ;
if ( ! response || ! Array . isArray ( response ) || response . length === 0 ) {
cardsContainer . innerHTML = "<p>No cards found.</p>" ;
return ;
}
2024-12-17 03:53:37 +00:00
// Validate cards and filter
2024-12-13 01:23:34 +00:00
const validatedCards = await Promise . all (
response . map ( async card => {
const isValid = await validateCardStructure ( card ) ;
return isValid ? card : null ;
} )
) ;
const validCards = validatedCards . filter ( card => card !== null ) ;
if ( validCards . length === 0 ) {
cardsContainer . innerHTML = "<p>No valid cards found.</p>" ;
return ;
}
2024-12-17 03:53:37 +00:00
// Sort cards by timestamp descending (newest first)
2024-12-15 03:40:31 +00:00
validCards . sort ( ( a , b ) => {
2024-12-17 03:53:37 +00:00
const timestampA = a . updated || a . created || 0 ;
2024-12-15 03:40:31 +00:00
const timestampB = b . updated || b . created || 0 ;
2024-12-17 03:53:37 +00:00
return timestampB - timestampA ;
2024-12-15 03:40:31 +00:00
} ) ;
2024-12-17 03:53:37 +00:00
// Display skeleton cards immediately
2024-12-15 03:40:31 +00:00
cardsContainer . innerHTML = "" ;
2024-12-17 03:53:37 +00:00
validCards . forEach ( card => {
const skeletonHTML = createSkeletonCardHTML ( card . identifier ) ;
cardsContainer . insertAdjacentHTML ( "beforeend" , skeletonHTML ) ;
} ) ;
2024-12-15 03:40:31 +00:00
2024-12-17 03:53:37 +00:00
// Fetch and update each card
validCards . forEach ( async card => {
try {
const cardDataResponse = await qortalRequest ( {
action : "FETCH_QDN_RESOURCE" ,
name : card . name ,
service : "BLOG_POST" ,
identifier : card . identifier ,
} ) ;
if ( ! cardDataResponse ) {
console . warn ( ` Skipping invalid card: ${ JSON . stringify ( card ) } ` ) ;
removeSkeleton ( card . identifier ) ;
return ;
2024-12-13 01:23:34 +00:00
}
2024-12-17 03:53:37 +00:00
// Skip cards without polls
if ( ! cardDataResponse . poll ) {
console . warn ( ` Skipping card with no poll: ${ card . identifier } ` ) ;
removeSkeleton ( card . identifier ) ;
return ;
}
// Fetch poll results
const pollResults = await fetchPollResults ( cardDataResponse . poll ) ;
// Generate final card HTML
const finalCardHTML = await createCardHTML ( cardDataResponse , pollResults , card . identifier ) ;
replaceSkeleton ( card . identifier , finalCardHTML ) ;
} catch ( error ) {
console . error ( ` Error processing card ${ card . identifier } : ` , error ) ;
removeSkeleton ( card . identifier ) ; // Silently remove skeleton on error
}
} ) ;
2024-12-13 01:23:34 +00:00
} catch ( error ) {
console . error ( "Error loading cards:" , error ) ;
cardsContainer . innerHTML = "<p>Failed to load cards.</p>" ;
}
} ;
2024-12-17 03:53:37 +00:00
const removeSkeleton = ( cardIdentifier ) => {
const skeletonCard = document . getElementById ( ` skeleton- ${ cardIdentifier } ` ) ;
if ( skeletonCard ) {
skeletonCard . remove ( ) ; // Remove the skeleton silently
}
} ;
2024-12-13 01:23:34 +00:00
2024-12-17 03:53:37 +00:00
const replaceSkeleton = ( cardIdentifier , htmlContent ) => {
const skeletonCard = document . getElementById ( ` skeleton- ${ cardIdentifier } ` ) ;
if ( skeletonCard ) {
skeletonCard . outerHTML = htmlContent ;
}
} ;
// Function to create a skeleton card
const createSkeletonCardHTML = ( cardIdentifier ) => {
return `
< div id = "skeleton-${cardIdentifier}" class = "skeleton-card" style = "padding: 10px; border: 1px solid gray; margin: 10px 0;" >
< div style = "display: flex; align-items: center;" >
< div style = "width: 50px; height: 50px; background-color: #ccc; border-radius: 50%;" > < / d i v >
< div style = "margin-left: 10px;" >
< div style = "width: 120px; height: 20px; background-color: #ccc; margin-bottom: 5px;" > < / d i v >
< div style = "width: 80px; height: 15px; background-color: #ddd;" > < / d i v >
< / d i v >
< / d i v >
< div style = "margin-top: 10px;" >
< div style = "width: 100%; height: 40px; background-color: #eee;" > < / d i v >
< / d i v >
< / d i v >
` ;
} ;
2024-12-15 03:40:31 +00:00
2024-12-13 01:23:34 +00:00
// Function to check and fech an existing Minter Card if attempting to publish twice ----------------------------------------
const fetchExistingCard = async ( ) => {
2024-12-11 22:40:32 +00:00
try {
// Step 1: Perform the search
const response = await qortalRequest ( {
action : "SEARCH_QDN_RESOURCES" ,
service : "BLOG_POST" ,
identifier : cardIdentifierPrefix ,
2024-12-12 00:27:29 +00:00
name : userState . accountName ,
2024-12-13 01:23:34 +00:00
mode : "ALL" ,
exactMatchNames : true // Search for the exact userName only when finding existing cards
2024-12-11 22:40:32 +00:00
} ) ;
console . log ( ` SEARCH_QDN_RESOURCES response: ${ JSON . stringify ( response , null , 2 ) } ` ) ;
// Step 2: Check if the response is an array and not empty
if ( ! response || ! Array . isArray ( response ) || response . length === 0 ) {
console . log ( "No cards found for the current user." ) ;
return null ;
}
2024-12-13 01:23:34 +00:00
// Step 3: Validate cards asynchronously
const validatedCards = await Promise . all (
response . map ( async card => {
const isValid = await validateCardStructure ( card ) ;
return isValid ? card : null ;
} )
) ;
// Step 4: Filter out invalid cards
const validCards = validatedCards . filter ( card => card !== null ) ;
2024-12-11 22:40:32 +00:00
if ( validCards . length > 0 ) {
2024-12-13 01:23:34 +00:00
// Step 5: Sort by most recent timestamp
2024-12-11 22:40:32 +00:00
const mostRecentCard = validCards . sort ( ( a , b ) => b . created - a . created ) [ 0 ] ;
2024-12-13 01:23:34 +00:00
// Step 6: Fetch full card data
const cardDataResponse = await qortalRequest ( {
action : "FETCH_QDN_RESOURCE" ,
name : userState . accountName , // User's account name
service : "BLOG_POST" ,
identifier : mostRecentCard . identifier
} ) ;
2024-12-11 22:40:32 +00:00
2024-12-13 01:23:34 +00:00
existingCardIdentifier = mostRecentCard . identifier ;
existingCardData = cardDataResponse ;
2024-12-11 22:40:32 +00:00
2024-12-13 01:23:34 +00:00
console . log ( "Full card data fetched successfully:" , cardDataResponse ) ;
2024-12-11 22:40:32 +00:00
2024-12-13 01:23:34 +00:00
return cardDataResponse ;
}
2024-12-11 22:40:32 +00:00
2024-12-13 01:23:34 +00:00
console . log ( "No valid cards found." ) ;
return null ;
2024-12-11 22:40:32 +00:00
} catch ( error ) {
console . error ( "Error fetching existing card:" , error ) ;
return null ;
}
2024-12-13 01:23:34 +00:00
} ;
// Validate that a card is indeed a card and not a comment. -------------------------------------
const validateCardStructure = async ( card ) => {
2024-12-11 22:40:32 +00:00
return (
typeof card === "object" &&
card . name &&
card . service === "BLOG_POST" &&
card . identifier && ! card . identifier . includes ( "comment" ) &&
card . created
) ;
}
2024-12-13 01:23:34 +00:00
// Load existing card data passed, into the form for editing -------------------------------------
const loadCardIntoForm = async ( cardData ) => {
console . log ( "Loading existing card data:" , cardData ) ;
2024-12-11 22:40:32 +00:00
document . getElementById ( "card-header" ) . value = cardData . header ;
document . getElementById ( "card-content" ) . value = cardData . content ;
const linksContainer = document . getElementById ( "links-container" ) ;
linksContainer . innerHTML = "" ; // Clear previous links
cardData . links . forEach ( link => {
const linkInput = document . createElement ( "input" ) ;
linkInput . type = "text" ;
linkInput . className = "card-link" ;
linkInput . value = link ;
linksContainer . appendChild ( linkInput ) ;
} ) ;
}
2024-12-13 01:23:34 +00:00
// Main function to publish a new Minter Card -----------------------------------------------
const publishCard = async ( ) => {
2024-12-11 22:40:32 +00:00
const header = document . getElementById ( "card-header" ) . value . trim ( ) ;
const content = document . getElementById ( "card-content" ) . value . trim ( ) ;
const links = Array . from ( document . querySelectorAll ( ".card-link" ) )
. map ( input => input . value . trim ( ) )
. filter ( link => link . startsWith ( "qortal://" ) ) ;
if ( ! header || ! content ) {
alert ( "Header and content are required!" ) ;
return ;
}
2024-12-12 00:27:29 +00:00
const cardIdentifier = isExistingCard ? existingCardIdentifier : ` ${ cardIdentifierPrefix } - ${ await uid ( ) } ` ;
2024-12-11 22:40:32 +00:00
const pollName = ` ${ cardIdentifier } -poll ` ;
const pollDescription = ` Mintership Board Poll for ${ userState . accountName } ` ;
const cardData = {
header ,
content ,
links ,
creator : userState . accountName ,
timestamp : Date . now ( ) ,
poll : pollName ,
} ;
2024-12-13 01:23:34 +00:00
2024-12-11 22:40:32 +00:00
try {
let base64CardData = await objectToBase64 ( cardData ) ;
if ( ! base64CardData ) {
console . log ( ` initial base64 object creation with objectToBase64 failed, using btoa... ` ) ;
base64CardData = btoa ( JSON . stringify ( cardData ) ) ;
}
await qortalRequest ( {
action : "PUBLISH_QDN_RESOURCE" ,
name : userState . accountName ,
service : "BLOG_POST" ,
identifier : cardIdentifier ,
data64 : base64CardData ,
} ) ;
2024-12-12 00:41:20 +00:00
if ( ! isExistingCard ) {
await qortalRequest ( {
action : "CREATE_POLL" ,
pollName ,
pollDescription ,
pollOptions : [ 'Yes, No' ] ,
pollOwnerAddress : userState . accountAddress ,
} ) ;
2024-12-11 22:40:32 +00:00
2024-12-12 00:41:20 +00:00
alert ( "Card and poll published successfully!" ) ;
}
if ( isExistingCard ) {
alert ( "Card Updated Successfully! (No poll updates are possible at this time...)" )
}
2024-12-11 22:40:32 +00:00
document . getElementById ( "publish-card-form" ) . reset ( ) ;
document . getElementById ( "publish-card-view" ) . style . display = "none" ;
document . getElementById ( "cards-container" ) . style . display = "flex" ;
await loadCards ( ) ;
} catch ( error ) {
console . error ( "Error publishing card or poll:" , error ) ;
alert ( "Failed to publish card and poll." ) ;
}
}
2024-12-13 01:23:34 +00:00
//Calculate the poll results passed from other functions with minterGroupMembers and minterAdmins ---------------------------
const calculatePollResults = async ( pollData , minterGroupMembers , minterAdmins ) => {
const memberAddresses = minterGroupMembers . map ( member => member . member )
2024-12-15 03:40:31 +00:00
const minterAdminAddresses = minterAdmins . map ( member => member . member )
const adminGroupsMembers = await fetchAllAdminGroupsMembers ( )
const groupAdminAddresses = adminGroupsMembers . map ( member => member . member )
const adminAddresses = [ ] ;
adminAddresses . push ( ... minterAdminAddresses , ... groupAdminAddresses ) ;
2024-12-12 01:44:04 +00:00
2024-12-13 01:23:34 +00:00
let adminYes = 0 , adminNo = 0 , minterYes = 0 , minterNo = 0 , yesWeight = 0 , noWeight = 0
2024-12-11 22:40:32 +00:00
2024-12-13 01:23:34 +00:00
pollData . voteWeights . forEach ( weightData => {
if ( weightData . optionName === 'Yes' ) {
yesWeight = weightData . voteWeight
} else if ( weightData . optionName === 'No' ) {
noWeight = weightData . voteWeight
2024-12-11 22:40:32 +00:00
}
2024-12-13 01:23:34 +00:00
} )
2024-12-11 22:40:32 +00:00
2024-12-13 01:23:34 +00:00
for ( const vote of pollData . votes ) {
const voterAddress = await getAddressFromPublicKey ( vote . voterPublicKey )
console . log ( ` voter address: ${ voterAddress } ` )
2024-12-11 22:40:32 +00:00
2024-12-12 02:32:48 +00:00
if ( vote . optionIndex === 0 ) {
2024-12-13 01:23:34 +00:00
adminAddresses . includes ( voterAddress ) ? adminYes ++ : memberAddresses . includes ( voterAddress ) ? minterYes ++ : console . log ( ` voter ${ voterAddress } is not a minter nor an admin...Not including results... ` )
2024-12-12 02:32:48 +00:00
} else if ( vote . optionIndex === 1 ) {
2024-12-13 01:23:34 +00:00
adminAddresses . includes ( voterAddress ) ? adminNo ++ : memberAddresses . includes ( voterAddress ) ? minterNo ++ : console . log ( ` voter ${ voterAddress } is not a minter nor an admin...Not including results... ` )
2024-12-11 22:40:32 +00:00
}
2024-12-13 01:23:34 +00:00
}
2024-12-11 22:40:32 +00:00
2024-12-13 01:23:34 +00:00
// TODO - create a new function to calculate the weights of each voting MINTER only.
// This will give ALL weight whether voter is in minter group or not...
// until that is changed on the core we must calculate manually.
const totalYesWeight = yesWeight
const totalNoWeight = noWeight
2024-12-11 22:40:32 +00:00
2024-12-13 01:23:34 +00:00
const totalYes = adminYes + minterYes
const totalNo = adminNo + minterNo
return { adminYes , adminNo , minterYes , minterNo , totalYes , totalNo , totalYesWeight , totalNoWeight }
}
2024-12-11 22:40:32 +00:00
2024-12-13 01:23:34 +00:00
// Post a comment on a card. ---------------------------------
2024-12-11 22:40:32 +00:00
const postComment = async ( cardIdentifier ) => {
const commentInput = document . getElementById ( ` new-comment- ${ cardIdentifier } ` ) ;
const commentText = commentInput . value . trim ( ) ;
if ( ! commentText ) {
alert ( 'Comment cannot be empty!' ) ;
return ;
}
const commentData = {
content : commentText ,
creator : userState . accountName ,
timestamp : Date . now ( ) ,
} ;
const commentIdentifier = ` comment- ${ cardIdentifier } - ${ await uid ( ) } ` ;
try {
const base64CommentData = await objectToBase64 ( commentData ) ;
if ( ! base64CommentData ) {
console . log ( ` initial base64 object creation with objectToBase64 failed, using btoa... ` ) ;
base64CommentData = btoa ( JSON . stringify ( commentData ) ) ;
}
2024-12-13 01:23:34 +00:00
2024-12-11 22:40:32 +00:00
await qortalRequest ( {
action : 'PUBLISH_QDN_RESOURCE' ,
name : userState . accountName ,
service : 'BLOG_POST' ,
identifier : commentIdentifier ,
data64 : base64CommentData ,
} ) ;
2024-12-12 00:41:20 +00:00
2024-12-11 22:40:32 +00:00
alert ( 'Comment posted successfully!' ) ;
commentInput . value = '' ; // Clear input
2024-12-15 03:40:31 +00:00
// await displayComments(cardIdentifier); // Refresh comments - We don't need to do this as comments will be displayed only after confirmation.
2024-12-11 22:40:32 +00:00
} catch ( error ) {
console . error ( 'Error posting comment:' , error ) ;
alert ( 'Failed to post comment.' ) ;
}
} ;
2024-12-13 01:23:34 +00:00
//Fetch the comments for a card with passed card identifier ----------------------------
2024-12-11 22:40:32 +00:00
const fetchCommentsForCard = async ( cardIdentifier ) => {
try {
const response = await qortalRequest ( {
action : 'SEARCH_QDN_RESOURCES' ,
service : 'BLOG_POST' ,
query : ` comment- ${ cardIdentifier } ` ,
2024-12-13 01:23:34 +00:00
mode : "ALL"
2024-12-11 22:40:32 +00:00
} ) ;
return response ;
} catch ( error ) {
console . error ( ` Error fetching comments for ${ cardIdentifier } : ` , error ) ;
return [ ] ;
}
} ;
2024-12-13 01:23:34 +00:00
// display the comments on the card, with passed cardIdentifier to identify the card --------------
2024-12-11 22:40:32 +00:00
const displayComments = async ( cardIdentifier ) => {
try {
const comments = await fetchCommentsForCard ( cardIdentifier ) ;
const commentsContainer = document . getElementById ( ` comments-container- ${ cardIdentifier } ` ) ;
// Fetch and display each comment
for ( const comment of comments ) {
const commentDataResponse = await qortalRequest ( {
action : "FETCH_QDN_RESOURCE" ,
name : comment . name ,
service : "BLOG_POST" ,
identifier : comment . identifier ,
} ) ;
const timestamp = await timestampToHumanReadableDate ( commentDataResponse . timestamp ) ;
2024-12-13 01:23:34 +00:00
//TODO - add fetching of poll results and checking to see if the commenter has voted and display it as 'supports minter' section.
2024-12-11 22:40:32 +00:00
const commentHTML = `
< div class = "comment" style = "border: 1px solid gray; margin: 1vh 0; padding: 1vh; background: #1c1c1c;" >
< p > < strong > < u > $ { commentDataResponse . creator } < / s t r o n g > : < / p > < / u >
< p > $ { commentDataResponse . content } < / p >
< p > < i > $ { timestamp } < /p></i >
< / d i v >
` ;
commentsContainer . insertAdjacentHTML ( 'beforeend' , commentHTML ) ;
}
} catch ( error ) {
console . error ( ` Error displaying comments for ${ cardIdentifier } : ` , error ) ;
alert ( "Failed to load comments. Please try again." ) ;
}
} ;
2024-12-13 01:23:34 +00:00
// Toggle comments from being shown or not, with passed cardIdentifier for comments being toggled --------------------
2024-12-11 22:40:32 +00:00
const toggleComments = async ( cardIdentifier ) => {
const commentsSection = document . getElementById ( ` comments-section- ${ cardIdentifier } ` ) ;
if ( commentsSection . style . display === 'none' || ! commentsSection . style . display ) {
await displayComments ( cardIdentifier ) ;
commentsSection . style . display = 'block' ;
} else {
commentsSection . style . display = 'none' ;
}
} ;
2024-12-13 01:23:34 +00:00
const createModal = async ( ) => {
const modalHTML = `
< div id = "modal" style = "display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); z-index: 1000;" >
2024-12-13 05:49:14 +00:00
< div style = "position: relative; margin: 10% auto; width: 95%; height: 80%; background: white; border-radius: 10px; overflow: hidden;" >
2024-12-13 01:23:34 +00:00
< iframe id = "modalContent" src = "" style = "width: 100%; height: 100%; border: none;" > < / i f r a m e >
< button onclick = "closeModal()" style = "position: absolute; top: 10px; right: 10px; background: red; color: white; border: none; padding: 5px 10px; border-radius: 5px;" > Close < / b u t t o n >
< / d i v >
< / d i v >
` ;
document . body . insertAdjacentHTML ( 'beforeend' , modalHTML ) ;
}
// Function to open the modal
const openModal = async ( link ) => {
const processedLink = await processLink ( link ) // Process the link to replace `qortal://` for rendering in modal
const modal = document . getElementById ( 'modal' ) ;
const modalContent = document . getElementById ( 'modalContent' ) ;
modalContent . src = processedLink ; // Set the iframe source to the link
modal . style . display = 'block' ; // Show the modal
}
// Function to close the modal
const closeModal = async ( ) => {
const modal = document . getElementById ( 'modal' ) ;
const modalContent = document . getElementById ( 'modalContent' ) ;
modal . style . display = 'none' ; // Hide the modal
modalContent . src = '' ; // Clear the iframe source
}
const processLink = async ( link ) => {
if ( link . startsWith ( 'qortal://' ) ) {
2024-12-13 05:49:14 +00:00
const match = link . match ( /^qortal:\/\/([^/]+)(\/.*)?$/ ) ;
if ( match ) {
const firstParam = match [ 1 ] . toUpperCase ( ) ; // Convert to uppercase
const remainingPath = match [ 2 ] || "" ; // Rest of the URL
// Perform any asynchronous operation if necessary
await new Promise ( resolve => setTimeout ( resolve , 10 ) ) ; // Simulating async operation
2024-12-13 20:10:27 +00:00
return ` /render/ ${ firstParam } ${ remainingPath } ` ;
2024-12-13 05:49:14 +00:00
}
2024-12-13 01:23:34 +00:00
}
2024-12-13 05:49:14 +00:00
return link ; // Return unchanged if not a Qortal link
2024-12-13 01:23:34 +00:00
}
// Create the overall Minter Card HTML -----------------------------------------------
const createCardHTML = async ( cardData , pollResults , cardIdentifier ) => {
2024-12-11 22:40:32 +00:00
const { header , content , links , creator , timestamp , poll } = cardData ;
const formattedDate = new Date ( timestamp ) . toLocaleString ( ) ;
2024-12-12 01:00:55 +00:00
const avatarUrl = ` /arbitrary/THUMBNAIL/ ${ creator } /qortal_avatar ` ;
2024-12-11 22:40:32 +00:00
const linksHTML = links . map ( ( link , index ) => `
2024-12-13 01:23:34 +00:00
< button onclick = "openModal('${link}')" >
2024-12-11 22:40:32 +00:00
$ { ` Link ${ index + 1 } - ${ link } ` }
< / b u t t o n >
` ).join("");
const minterGroupMembers = await fetchMinterGroupMembers ( ) ;
2024-12-13 01:23:34 +00:00
const minterAdmins = await fetchMinterGroupAdmins ( ) ;
const { adminYes = 0 , adminNo = 0 , minterYes = 0 , minterNo = 0 , totalYes = 0 , totalNo = 0 , totalYesWeight = 0 , totalNoWeight = 0 } = await calculatePollResults ( pollResults , minterGroupMembers , minterAdmins )
await createModal ( )
2024-12-11 22:40:32 +00:00
return `
< div class = "minter-card" >
< div class = "minter-card-header" >
2024-12-12 01:44:04 +00:00
< img src = "${avatarUrl}" alt = "User Avatar" class = "user-avatar" style = "width: 50px; height: 50px; border-radius: 50%; align-self: center;" >
2024-12-11 22:40:32 +00:00
< h3 > $ { creator } < / h 3 >
< p > $ { header } < / p >
< / d i v >
< div class = "support-header" > < h5 > Minter Post : < / h 5 > < / d i v >
< div class = "info" >
$ { content }
< / d i v >
< div class = "support-header" > < h5 > Minter Links : < / h 5 > < / d i v >
< div class = "info-links" >
$ { linksHTML }
< / d i v >
< div class = "results-header support-header" > < h5 > Current Results : < / h 5 > < / d i v >
< div class = "minter-card-results" >
< div class = "admin-results" >
< span class = "admin-yes" > Admin Yes : $ { adminYes } < / s p a n >
< span class = "admin-no" > Admin No : $ { adminNo } < / s p a n >
< / d i v >
< div class = "minter-results" >
< span class = "minter-yes" > Minter Yes : $ { minterYes } < / s p a n >
< span class = "minter-no" > Minter No : $ { minterNo } < / s p a n >
< / d i v >
< div class = "total-results" >
< span class = "total-yes" > Total Yes : $ { totalYes } < / s p a n >
< span class = "total-no" > Total No : $ { totalNo } < / s p a n >
< / d i v >
< / d i v >
< div class = "support-header" > < h5 > Support Minter ? < / h 5 > < / d i v >
< div class = "actions" >
< div class = "actions-buttons" >
2024-12-12 02:32:48 +00:00
< button class = "yes" onclick = "voteYesOnPoll('${poll}')" > YES < / b u t t o n >
2024-12-11 22:40:32 +00:00
< button class = "comment" onclick = "toggleComments('${cardIdentifier}')" > COMMENTS < / b u t t o n >
2024-12-12 02:32:48 +00:00
< button class = "no" onclick = "voteNoOnPoll('${poll}')" > NO < / b u t t o n >
2024-12-11 22:40:32 +00:00
< / d i v >
< / d i v >
< div id = "comments-section-${cardIdentifier}" class = "comments-section" style = "display: none; margin-top: 20px;" >
< div id = "comments-container-${cardIdentifier}" class = "comments-container" > < / d i v >
< textarea id = "new-comment-${cardIdentifier}" placeholder = "Write a comment..." style = "width: 100%; margin-top: 10px;" > < / t e x t a r e a >
< button onclick = "postComment('${cardIdentifier}')" > Post Comment < / b u t t o n >
< / d i v >
< p style = "font-size: 12px; color: gray;" > Published by : $ { creator } on $ { formattedDate } < / p >
< / d i v >
` ;
}