2024-12-19 03:39:15 +00:00
// NOTE - Change isTestMode to false prior to actual release ---- !important - You may also change identifier if you want to not show older cards.
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
const isEncryptedTestMode = false
2024-12-24 08:27:17 +00:00
const encryptedCardIdentifierPrefix = "card-MAC"
2024-12-20 05:28:36 +00:00
let isExistingEncryptedCard = false
let existingDecryptedCardData = { }
let existingEncryptedCardIdentifier = { }
2024-12-19 03:39:15 +00:00
let cardMinterName = { }
let existingCardMinterNames = [ ]
2024-12-24 08:27:17 +00:00
let isTopic = false
let attemptLoadAdminDataCount = 0
let adminMemberCount = 0
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
let adminPublicKeys = [ ]
2024-12-19 03:39:15 +00:00
console . log ( "Attempting to load AdminBoard.js" ) ;
const loadAdminBoardPage = async ( ) => {
// 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;" > AdminBoard < / h 1 >
< p style = "font-size: 1.25em;" > The Admin Board is an encrypted card publishing board to keep track of minter data for the Minter Admins . Any Admin may publish a card , and related data , make comments on existing cards , and vote on existing card data in support or not of the name on the card . It is essentially a 'project management' tool to assist the Minter Admins in keeping track of the data related to minters they are adding / removing from the minter group . < / p >
< p > More functionality will be added over time . One of the first features will be the ability to output the existing card data 'decisions' , to a json formatted list in order to allow crowetic to run his script easily until the final Mintership proposal changes are completed , and the MINTER group is transferred to 'null' . < / p >
< button id = "publish-card-button" class = "publish-card-button" style = "margin: 20px; padding: 10px;" > Publish Encrypted Card < / b u t t o n >
< button id = "refresh-cards-button" class = "refresh-cards-button" style = "padding: 10px;" > Refresh Cards < / b u t t o n >
< div id = "encrypted-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 >
2024-12-24 08:27:17 +00:00
< div class = "publish-card-checkbox" style = "margin-top: 1em;" >
< input type = "checkbox" id = "topic-checkbox" name = "topicMode" / >
< label for = "topic-checkbox" > Is this a Topic instead of a Minter ? < / l a b e l >
< / d i v >
< label for = "minter-name-input" > Input Topic or Minter Name : < / l a b e l >
< input type = "text" id = "minter-name-input" maxlength = "100" placeholder = "Enter Topic or Minter's Name" required >
2024-12-19 03:39:15 +00:00
< label for = "card-header" > Header : < / l a b e l >
< input type = "text" id = "card-header" maxlength = "100" placeholder = "Explain main point/issue" required >
< label for = "card-content" > Content : < / l a b e l >
2024-12-24 08:27:17 +00:00
< textarea id = "card-content" placeholder = "Enter any information you like...You may also attach links to more in-depth information, etc. (Links will pop-up in a custom in-app viewer upon being clicked.)" required > < / t e x t a r e a >
2024-12-19 03:39:15 +00:00
< 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 ) ;
const publishCardButton = document . getElementById ( "publish-card-button" )
if ( publishCardButton ) {
publishCardButton . addEventListener ( "click" , async ( ) => {
const publishCardView = document . getElementById ( "publish-card-view" )
publishCardView . style . display = "flex"
document . getElementById ( "encrypted-cards-container" ) . style . display = "none"
} )
}
const refreshCardsButton = document . getElementById ( "refresh-cards-button" )
if ( refreshCardsButton ) {
refreshCardsButton . addEventListener ( "click" , async ( ) => {
const encryptedCardsContainer = document . getElementById ( "encrypted-cards-container" )
encryptedCardsContainer . innerHTML = "<p>Refreshing cards...</p>"
await fetchAllEncryptedCards ( )
} )
}
const cancelPublishButton = document . getElementById ( "cancel-publish-button" )
if ( cancelPublishButton ) {
cancelPublishButton . addEventListener ( "click" , async ( ) => {
const encryptedCardsContainer = document . getElementById ( "encrypted-cards-container" )
encryptedCardsContainer . style . display = "flex" ; // Restore visibility
const publishCardView = document . getElementById ( "publish-card-view" )
publishCardView . style . display = "none" ; // Hide the publish form
} )
}
const addLinkButton = document . getElementById ( "add-link-button" )
if ( addLinkButton ) {
addLinkButton . addEventListener ( "click" , async ( ) => {
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 ( ) ;
2024-12-24 08:27:17 +00:00
const isTopicChecked = document . getElementById ( "topic-checkbox" ) . checked ;
// Pass that boolean to publishEncryptedCard
await publishEncryptedCard ( isTopicChecked ) ;
2024-12-19 03:39:15 +00:00
} ) ;
2024-12-20 05:28:36 +00:00
// await fetchAndValidateAllAdminCards();
2024-12-19 03:39:15 +00:00
await fetchAllEncryptedCards ( ) ;
2024-12-24 08:27:17 +00:00
await updateOrSaveAdminGroupsDataLocally ( ) ;
}
// Example: fetch and save admin public keys and count
const updateOrSaveAdminGroupsDataLocally = async ( ) => {
try {
// Fetch the array of admin public keys
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
const verifiedAdminPublicKeys = await fetchAdminGroupsMembersPublicKeys ( )
2024-12-24 08:27:17 +00:00
// Build an object containing the count and the array
const adminData = {
keysCount : verifiedAdminPublicKeys . length ,
publicKeys : verifiedAdminPublicKeys
} ;
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
adminPublicKeys = verifiedAdminPublicKeys
2024-12-24 08:27:17 +00:00
// Stringify and save to localStorage
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
localStorage . setItem ( 'savedAdminData' , JSON . stringify ( adminData ) )
2024-12-24 08:27:17 +00:00
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
console . log ( 'Admin public keys saved locally:' , adminData )
2024-12-24 08:27:17 +00:00
} catch ( error ) {
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
console . error ( 'Error fetching/storing admin public keys:' , error )
2024-12-24 08:27:17 +00:00
attemptLoadAdminDataCount ++
}
} ;
const loadOrFetchAdminGroupsData = async ( ) => {
try {
// Pull the JSON from localStorage
const storedData = localStorage . getItem ( 'savedAdminData' )
if ( ! storedData && attemptLoadAdminDataCount <= 3 ) {
console . log ( 'No saved admin public keys found in local storage. Fetching...' )
await updateOrSaveAdminGroupsDataLocally ( )
attemptLoadAdminDataCount ++
return null ;
}
// Parse the JSON, then store the global variables.
const parsedData = JSON . parse ( storedData )
adminMemberCount = parsedData . keysCount
adminPublicKeys = parsedData . publicKeys
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
console . log ( typeof adminPublicKeys ) ; // Should be "object"
console . log ( Array . isArray ( adminPublicKeys ) )
console . log ( ` Loaded admins 'keysCount'= ${ adminMemberCount } , publicKeys= ` , adminPublicKeys )
2024-12-24 08:27:17 +00:00
attemptLoadAdminDataCount = 0
return parsedData ; // and return { adminMemberCount, adminKeys } to the caller
} catch ( error ) {
console . error ( 'Error loading/parsing saved admin public keys:' , error )
return null
}
2024-12-19 03:39:15 +00:00
}
const extractCardsMinterName = ( cardIdentifier ) => {
// Ensure the identifier starts with the prefix
if ( ! cardIdentifier . startsWith ( ` ${ encryptedCardIdentifierPrefix } - ` ) ) {
throw new Error ( 'Invalid identifier format or prefix mismatch' ) ;
}
// Split the identifier into parts
const parts = cardIdentifier . split ( '-' ) ;
// Ensure the format has at least 3 parts
if ( parts . length < 3 ) {
throw new Error ( 'Invalid identifier format' ) ;
}
2024-12-24 08:27:17 +00:00
if ( parts . slice ( 2 , - 1 ) . join ( '-' ) === 'TOPIC' ) {
console . log ( ` TOPIC found in identifier: ${ cardIdentifier } - not including in duplicatesList ` )
return
}
2024-12-19 03:39:15 +00:00
// Extract minterName (everything from the second part to the second-to-last part)
const minterName = parts . slice ( 2 , - 1 ) . join ( '-' ) ;
// Return the extracted minterName
return minterName ;
}
2024-12-20 05:28:36 +00:00
const processCards = async ( validEncryptedCards ) => {
const latestCardsMap = new Map ( )
2024-12-19 03:39:15 +00:00
2024-12-20 05:28:36 +00:00
// Step 1: Filter and keep the most recent card per identifier
validEncryptedCards . forEach ( card => {
const timestamp = card . updated || card . created || 0
const existingCard = latestCardsMap . get ( card . identifier )
2024-12-19 03:39:15 +00:00
2024-12-20 05:28:36 +00:00
if ( ! existingCard || timestamp > ( existingCard . updated || existingCard . created || 0 ) ) {
latestCardsMap . set ( card . identifier , card )
}
} )
2024-12-19 03:39:15 +00:00
2024-12-20 05:28:36 +00:00
// Step 2: Extract unique cards
const uniqueValidCards = Array . from ( latestCardsMap . values ( ) )
2024-12-19 03:39:15 +00:00
2024-12-20 05:28:36 +00:00
// Step 3: Group by minterName and select the most recent card per minterName
const minterNameMap = new Map ( )
2024-12-19 03:39:15 +00:00
2024-12-20 05:28:36 +00:00
for ( const card of validEncryptedCards ) {
const minterName = await extractCardsMinterName ( card . identifier )
const existingCard = minterNameMap . get ( minterName )
const cardTimestamp = card . updated || card . created || 0
const existingTimestamp = existingCard ? . updated || existingCard ? . created || 0
2024-12-19 03:39:15 +00:00
if ( ! existingCardMinterNames . includes ( minterName ) ) {
existingCardMinterNames . push ( minterName )
console . log ( ` cardsMinterName: ${ minterName } - added to list ` )
}
2024-12-20 05:28:36 +00:00
// Keep only the most recent card for each minterName
if ( ! existingCard || cardTimestamp > existingTimestamp ) {
minterNameMap . set ( minterName , card )
}
2024-12-19 03:39:15 +00:00
}
2024-12-20 05:28:36 +00:00
// Step 4: Filter cards to ensure each minterName is included only once
const finalCards = [ ]
const seenMinterNames = new Set ( )
for ( const [ minterName , card ] of minterNameMap . entries ( ) ) {
if ( ! seenMinterNames . has ( minterName ) ) {
finalCards . push ( card )
seenMinterNames . add ( minterName ) // Mark the minterName as seen
}
}
// Step 5: Sort by the most recent timestamp
finalCards . sort ( ( a , b ) => {
const timestampA = a . updated || a . created || 0
const timestampB = b . updated || b . created || 0
return timestampB - timestampA
} )
return finalCards
}
2024-12-19 03:39:15 +00:00
//Main function to load the Minter Cards ----------------------------------------
const fetchAllEncryptedCards = async ( ) => {
const encryptedCardsContainer = document . getElementById ( "encrypted-cards-container" ) ;
encryptedCardsContainer . innerHTML = "<p>Loading cards...</p>" ;
try {
const response = await qortalRequest ( {
action : "SEARCH_QDN_RESOURCES" ,
service : "MAIL_PRIVATE" ,
query : encryptedCardIdentifierPrefix ,
mode : "ALL"
} ) ;
if ( ! response || ! Array . isArray ( response ) || response . length === 0 ) {
encryptedCardsContainer . innerHTML = "<p>No cards found.</p>" ;
return ;
}
// Validate cards and filter
const validatedEncryptedCards = await Promise . all (
response . map ( async card => {
const isValid = await validateEncryptedCardIdentifier ( card ) ;
return isValid ? card : null ;
} )
) ;
const validEncryptedCards = validatedEncryptedCards . filter ( card => card !== null ) ;
2024-12-20 05:28:36 +00:00
2024-12-19 03:39:15 +00:00
if ( validEncryptedCards . length === 0 ) {
encryptedCardsContainer . innerHTML = "<p>No valid cards found.</p>" ;
return ;
}
2024-12-20 05:28:36 +00:00
const finalCards = await processCards ( validEncryptedCards )
2024-12-19 03:39:15 +00:00
// Display skeleton cards immediately
encryptedCardsContainer . innerHTML = "" ;
2024-12-20 05:28:36 +00:00
finalCards . forEach ( card => {
2024-12-19 03:39:15 +00:00
const skeletonHTML = createSkeletonCardHTML ( card . identifier ) ;
encryptedCardsContainer . insertAdjacentHTML ( "beforeend" , skeletonHTML ) ;
} ) ;
// Fetch and update each card
2024-12-20 05:28:36 +00:00
finalCards . forEach ( async card => {
2024-12-19 03:39:15 +00:00
try {
const cardDataResponse = await qortalRequest ( {
action : "FETCH_QDN_RESOURCE" ,
name : card . name ,
service : "MAIL_PRIVATE" ,
identifier : card . identifier ,
encoding : "base64"
} ) ;
if ( ! cardDataResponse ) {
console . warn ( ` Skipping invalid card: ${ JSON . stringify ( card ) } ` ) ;
removeSkeleton ( card . identifier ) ;
return ;
}
const decryptedCardData = await decryptAndParseObject ( cardDataResponse ) ;
// Skip cards without polls
if ( ! decryptedCardData . poll ) {
console . warn ( ` Skipping card with no poll: ${ card . identifier } ` ) ;
removeSkeleton ( card . identifier ) ;
return ;
}
// Fetch poll results
const pollResults = await fetchPollResults ( decryptedCardData . poll ) ;
2024-12-24 08:27:17 +00:00
// const minterNameFromIdentifier = await extractCardsMinterName(card.identifier);
2024-12-21 06:07:18 +00:00
const encryptedCommentCount = await getEncryptedCommentCount ( card . identifier ) ;
2024-12-19 03:39:15 +00:00
// Generate final card HTML
2024-12-24 08:27:17 +00:00
2024-12-21 06:07:18 +00:00
const finalCardHTML = await createEncryptedCardHTML ( decryptedCardData , pollResults , card . identifier , encryptedCommentCount ) ;
2024-12-19 03:39:15 +00:00
replaceEncryptedSkeleton ( card . identifier , finalCardHTML ) ;
} catch ( error ) {
console . error ( ` Error processing card ${ card . identifier } : ` , error ) ;
removeEncryptedSkeleton ( card . identifier ) ; // Silently remove skeleton on error
}
} ) ;
} catch ( error ) {
console . error ( "Error loading cards:" , error ) ;
encryptedCardsContainer . innerHTML = "<p>Failed to load cards.</p>" ;
}
} ;
const removeEncryptedSkeleton = ( cardIdentifier ) => {
const encryptedSkeletonCard = document . getElementById ( ` skeleton- ${ cardIdentifier } ` ) ;
if ( encryptedSkeletonCard ) {
encryptedSkeletonCard . remove ( ) ; // Remove the skeleton silently
}
} ;
const replaceEncryptedSkeleton = ( cardIdentifier , htmlContent ) => {
const encryptedSkeletonCard = document . getElementById ( ` skeleton- ${ cardIdentifier } ` ) ;
if ( encryptedSkeletonCard ) {
encryptedSkeletonCard . outerHTML = htmlContent ;
}
} ;
// Function to create a skeleton card
const createEncryptedSkeletonCardHTML = ( 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 >
` ;
} ;
// Function to check and fech an existing Minter Card if attempting to publish twice ----------------------------------------
const fetchExistingEncryptedCard = async ( minterName ) => {
try {
// Step 1: Perform the search
const response = await qortalRequest ( {
action : "SEARCH_QDN_RESOURCES" ,
service : "MAIL_PRIVATE" ,
identifier : encryptedCardIdentifierPrefix ,
query : minterName ,
mode : "ALL" ,
} ) ;
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 ;
}
// Step 3: Validate cards asynchronously
const validatedCards = await Promise . all (
response . map ( async card => {
const isValid = await validateEncryptedCardIdentifier ( card )
return isValid ? card : null ;
} )
) ;
// Step 4: Filter out invalid cards
const validCards = validatedCards . filter ( card => card !== null ) ;
if ( validCards . length > 0 ) {
// Step 5: Sort by most recent timestamp
const mostRecentCard = validCards . sort ( ( a , b ) => b . created - a . created ) [ 0 ] ;
// Step 6: Fetch full card data
const cardDataResponse = await qortalRequest ( {
action : "FETCH_QDN_RESOURCE" ,
name : mostRecentCard . name ,
service : mostRecentCard . service ,
identifier : mostRecentCard . identifier ,
encoding : "base64"
} ) ;
existingEncryptedCardIdentifier = mostRecentCard . identifier ;
existingDecryptedCardData = await decryptAndParseObject ( cardDataResponse )
console . log ( "Full card data fetched successfully:" , existingDecryptedCardData ) ;
return existingDecryptedCardData ;
}
console . log ( "No valid cards found." ) ;
return null ;
} catch ( error ) {
console . error ( "Error fetching existing card:" , error ) ;
return null ;
}
} ;
// Validate that a card is indeed a card and not a comment. -------------------------------------
const validateEncryptedCardIdentifier = async ( card ) => {
return (
typeof card === "object" &&
card . name &&
card . service === "MAIL_PRIVATE" &&
card . identifier && ! card . identifier . includes ( "comment" ) &&
card . created
) ;
}
// Load existing card data passed, into the form for editing -------------------------------------
const loadEncryptedCardIntoForm = async ( ) => {
if ( existingDecryptedCardData ) {
console . log ( "Loading existing card data:" , existingDecryptedCardData ) ;
document . getElementById ( "minter-name-input" ) . value = existingDecryptedCardData . minterName
document . getElementById ( "card-header" ) . value = existingDecryptedCardData . header
document . getElementById ( "card-content" ) . value = existingDecryptedCardData . content
const linksContainer = document . getElementById ( "links-container" ) ;
linksContainer . innerHTML = "" ; // Clear previous links
existingDecryptedCardData . links . forEach ( link => {
const linkInput = document . createElement ( "input" ) ;
linkInput . type = "text" ;
linkInput . className = "card-link" ;
linkInput . value = link ;
linksContainer . appendChild ( linkInput ) ;
} ) ;
}
}
const validateMinterName = async ( minterName ) => {
try {
const nameInfo = await getNameInfo ( minterName )
const name = nameInfo . name
return name
} catch ( error ) {
console . error ( ` extracting name from name info: ${ minterName } failed. ` , error )
}
}
2024-12-24 08:27:17 +00:00
const publishEncryptedCard = async ( isTopicModePassed = false ) => {
// If the user wants it to be a topic, we set global isTopic = true, else false
isTopic = isTopicModePassed ;
2024-12-19 03:39:15 +00:00
const minterNameInput = document . getElementById ( "minter-name-input" ) . value . trim ( ) ;
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://" ) ) ;
2024-12-24 08:27:17 +00:00
// Basic validation
2024-12-19 03:39:15 +00:00
if ( ! header || ! content ) {
alert ( "Header and Content are required!" ) ;
return ;
}
2024-12-24 08:27:17 +00:00
let publishedMinterName = minterNameInput ;
2024-12-19 03:39:15 +00:00
2024-12-24 08:27:17 +00:00
// If not topic mode, validate the user actually entered a valid Minter name
if ( ! isTopic ) {
publishedMinterName = await validateMinterName ( minterNameInput ) ;
if ( ! publishedMinterName ) {
alert ( ` " ${ minterNameInput } " doesn't seem to be a valid Minter name. Please check or use topic mode. ` ) ;
2024-12-19 03:39:15 +00:00
return ;
2024-12-24 08:27:17 +00:00
}
// Also check for existing card if not topic
if ( ! isExistingEncryptedCard && existingCardMinterNames . includes ( publishedMinterName ) ) {
const updateCard = confirm (
` Minter Name: ${ publishedMinterName } already has a card. Update or Cancel? `
) ;
if ( updateCard ) {
await fetchExistingEncryptedCard ( publishedMinterName ) ;
await loadEncryptedCardIntoForm ( ) ;
isExistingEncryptedCard = true ;
return ;
} else {
return ;
2024-12-19 03:39:15 +00:00
}
}
}
2024-12-24 08:27:17 +00:00
// Determine final card identifier
const newCardIdentifier = isTopic
? ` ${ encryptedCardIdentifierPrefix } -TOPIC- ${ await uid ( ) } `
: ` ${ encryptedCardIdentifierPrefix } - ${ publishedMinterName } - ${ await uid ( ) } ` ;
2024-12-19 03:39:15 +00:00
2024-12-24 08:27:17 +00:00
const cardIdentifier = isExistingEncryptedCard ? existingEncryptedCardIdentifier : newCardIdentifier ;
// Build cardData
const pollName = ` ${ cardIdentifier } -poll ` ;
2024-12-19 03:39:15 +00:00
const cardData = {
2024-12-24 08:27:17 +00:00
minterName : publishedMinterName ,
2024-12-19 03:39:15 +00:00
header ,
content ,
links ,
creator : userState . accountName ,
timestamp : Date . now ( ) ,
poll : pollName ,
2024-12-24 08:27:17 +00:00
topicMode : isTopic
2024-12-19 03:39:15 +00:00
} ;
2024-12-24 08:27:17 +00:00
try {
// Convert to base64 or fallback
2024-12-19 03:39:15 +00:00
let base64CardData = await objectToBase64 ( cardData ) ;
if ( ! base64CardData ) {
base64CardData = btoa ( JSON . stringify ( cardData ) ) ;
}
2024-12-24 08:27:17 +00:00
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
let verifiedAdminPublicKeys = adminPublicKeys
if ( ( ! verifiedAdminPublicKeys ) || verifiedAdminPublicKeys . length <= 5 || ! Array . isArray ( verifiedAdminPublicKeys ) ) {
console . log ( ` adminPublicKeys variable failed check, attempting to load from localStorage ` , adminPublicKeys )
const savedAdminData = localStorage . getItem ( 'savedAdminData' )
const parsedAdminData = JSON . parse ( savedAdminData )
const loadedAdminKeys = parsedAdminData . publicKeys
if ( ( ! loadedAdminKeys ) || ( ! Array . isArray ( loadedAdminKeys ) ) || ( loadedAdminKeys . length === 0 ) ) {
console . log ( 'loaded admin keys from localStorage failed, falling back to API call...' )
verifiedAdminPublicKeys = await fetchAdminGroupsMembersPublicKeys ( )
}
verifiedAdminPublicKeys = loadedAdminKeys
}
2024-12-24 08:27:17 +00:00
2024-12-19 03:39:15 +00:00
await qortalRequest ( {
action : "PUBLISH_QDN_RESOURCE" ,
name : userState . accountName ,
service : "MAIL_PRIVATE" ,
identifier : cardIdentifier ,
data64 : base64CardData ,
encrypt : true ,
publicKeys : verifiedAdminPublicKeys
} ) ;
2024-12-24 08:27:17 +00:00
// Possibly create a poll if it’ s a brand new card
if ( ! isExistingEncryptedCard ) {
await qortalRequest ( {
action : "CREATE_POLL" ,
pollName ,
pollDescription : ` Admin Board Poll Published By ${ userState . accountName } ` ,
pollOptions : [ "Yes, No" ] ,
pollOwnerAddress : userState . accountAddress
} ) ;
alert ( "Card and poll published successfully!" ) ;
// If it’ s a real Minter name, store it so we know we have a card for them
if ( ! isTopic ) {
existingCardMinterNames . push ( publishedMinterName ) ;
}
} else {
alert ( "Card updated successfully! (No poll updates possible currently...)" ) ;
2024-12-19 03:39:15 +00:00
}
2024-12-24 08:27:17 +00:00
// Cleanup UI
2024-12-19 03:39:15 +00:00
document . getElementById ( "publish-card-form" ) . reset ( ) ;
document . getElementById ( "publish-card-view" ) . style . display = "none" ;
document . getElementById ( "encrypted-cards-container" ) . style . display = "flex" ;
2024-12-24 08:27:17 +00:00
isTopic = false ; // reset global
2024-12-19 03:39:15 +00:00
} catch ( error ) {
console . error ( "Error publishing card or poll:" , error ) ;
alert ( "Failed to publish card and poll." ) ;
}
2024-12-24 08:27:17 +00:00
} ;
2024-12-19 03:39:15 +00:00
2024-12-21 06:07:18 +00:00
const getEncryptedCommentCount = async ( cardIdentifier ) => {
2024-12-20 05:28:36 +00:00
try {
const response = await qortalRequest ( {
action : 'SEARCH_QDN_RESOURCES' ,
service : 'MAIL_PRIVATE' ,
query : ` comment- ${ cardIdentifier } ` ,
mode : "ALL"
} ) ;
// Just return the count; no need to decrypt each comment here
return Array . isArray ( response ) ? response . length : 0 ;
} catch ( error ) {
console . error ( ` Error fetching comment count for ${ cardIdentifier } : ` , error ) ;
return 0 ;
}
} ;
2024-12-19 03:39:15 +00:00
// Post a comment on a card. ---------------------------------
const postEncryptedComment = async ( cardIdentifier ) => {
const commentInput = document . getElementById ( ` new-comment- ${ cardIdentifier } ` ) ;
const commentText = commentInput . value . trim ( ) ;
if ( ! commentText ) {
alert ( 'Comment cannot be empty!' ) ;
return ;
}
2024-12-20 05:28:36 +00:00
const postTimestamp = Date . now ( )
2024-12-19 03:39:15 +00:00
console . log ( ` timestmp to be posted: ${ postTimestamp } ` )
const commentData = {
content : commentText ,
creator : userState . accountName ,
timestamp : postTimestamp ,
} ;
const commentIdentifier = ` comment- ${ cardIdentifier } - ${ await uid ( ) } ` ;
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
if ( ! Array . isArray ( adminPublicKeys ) || ( adminPublicKeys . length === 0 ) || ( ! adminPublicKeys ) ) {
console . log ( 'adminPpublicKeys variable failed checks, calling for admin public keys from API (comment)' , adminPublicKeys )
const verifiedAdminPublicKeys = await fetchAdminGroupsMembersPublicKeys ( )
2024-12-19 03:39:15 +00:00
adminPublicKeys = verifiedAdminPublicKeys
2024-12-24 08:27:17 +00:00
}
2024-12-19 03:39:15 +00:00
try {
const base64CommentData = await objectToBase64 ( commentData ) ;
if ( ! base64CommentData ) {
console . log ( ` initial base64 object creation with objectToBase64 failed, using btoa... ` ) ;
base64CommentData = btoa ( JSON . stringify ( commentData ) ) ;
}
await qortalRequest ( {
action : "PUBLISH_QDN_RESOURCE" ,
name : userState . accountName ,
service : "MAIL_PRIVATE" ,
identifier : commentIdentifier ,
data64 : base64CommentData ,
encrypt : true ,
publicKeys : adminPublicKeys
} ) ;
alert ( 'Comment posted successfully!' ) ;
commentInput . value = '' ; // Clear input
} catch ( error ) {
console . error ( 'Error posting comment:' , error ) ;
alert ( 'Failed to post comment.' ) ;
}
} ;
//Fetch the comments for a card with passed card identifier ----------------------------
const fetchEncryptedComments = async ( cardIdentifier ) => {
try {
const response = await qortalRequest ( {
action : 'SEARCH_QDN_RESOURCES' ,
service : 'MAIL_PRIVATE' ,
query : ` comment- ${ cardIdentifier } ` ,
mode : "ALL"
} ) ;
return response ;
} catch ( error ) {
console . error ( ` Error fetching comments for ${ cardIdentifier } : ` , error ) ;
return [ ] ;
}
} ;
// display the comments on the card, with passed cardIdentifier to identify the card --------------
const displayEncryptedComments = async ( cardIdentifier ) => {
try {
const comments = await fetchEncryptedComments ( 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 : "MAIL_PRIVATE" ,
identifier : comment . identifier ,
encoding : "base64"
} ) ;
const decryptedCommentData = await decryptAndParseObject ( commentDataResponse )
2024-12-20 05:28:36 +00:00
const timestampCheck = comment . updated || comment . created || 0
const timestamp = await timestampToHumanReadableDate ( timestampCheck ) ;
2024-12-19 03:39:15 +00:00
//TODO - add fetching of poll results and checking to see if the commenter has voted and display it as 'supports minter' section.
const commentHTML = `
< div class = "comment" style = "border: 1px solid gray; margin: 1vh 0; padding: 1vh; background: #1c1c1c;" >
< p > < strong > < u > $ { decryptedCommentData . creator } < / s t r o n g > : < / p > < / u >
< p > $ { decryptedCommentData . 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." ) ;
}
} ;
const calculateAdminBoardPollResults = async ( pollData , minterGroupMembers , minterAdmins ) => {
const memberAddresses = minterGroupMembers . map ( member => member . member )
const minterAdminAddresses = minterAdmins . map ( member => member . member )
const adminGroupsMembers = await fetchAllAdminGroupsMembers ( )
const groupAdminAddresses = adminGroupsMembers . map ( member => member . member )
const adminAddresses = [ ] ;
adminAddresses . push ( ... minterAdminAddresses , ... groupAdminAddresses ) ;
let adminYes = 0 , adminNo = 0 , minterYes = 0 , minterNo = 0 , yesWeight = 0 , noWeight = 0
pollData . voteWeights . forEach ( weightData => {
if ( weightData . optionName === 'Yes' ) {
yesWeight = weightData . voteWeight
} else if ( weightData . optionName === 'No' ) {
noWeight = weightData . voteWeight
}
} )
for ( const vote of pollData . votes ) {
const voterAddress = await getAddressFromPublicKey ( vote . voterPublicKey )
console . log ( ` voter address: ${ voterAddress } ` )
if ( vote . optionIndex === 0 ) {
adminAddresses . includes ( voterAddress ) ? adminYes ++ : memberAddresses . includes ( voterAddress ) ? minterYes ++ : console . log ( ` voter ${ voterAddress } is not a minter nor an admin...Not including results... ` )
} else if ( vote . optionIndex === 1 ) {
adminAddresses . includes ( voterAddress ) ? adminNo ++ : memberAddresses . includes ( voterAddress ) ? minterNo ++ : console . log ( ` voter ${ voterAddress } is not a minter nor an admin...Not including results... ` )
}
}
// 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
const totalYes = adminYes + minterYes
const totalNo = adminNo + minterNo
return { adminYes , adminNo , minterYes , minterNo , totalYes , totalNo , totalYesWeight , totalNoWeight }
}
const toggleEncryptedComments = async ( cardIdentifier ) => {
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
const commentsSection = document . getElementById ( ` comments-section- ${ cardIdentifier } ` )
const commentButton = document . getElementById ( ` comment-button- ${ cardIdentifier } ` )
if ( ! commentsSection || ! commentButton ) return ;
const count = commentButton . dataset . commentCount ;
const isHidden = ( commentsSection . style . display === 'none' || ! commentsSection . style . display ) ;
if ( isHidden ) {
// Show comments
commentButton . textContent = "LOADING..." ;
2024-12-19 03:39:15 +00:00
await displayEncryptedComments ( cardIdentifier ) ;
commentsSection . style . display = 'block' ;
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
// Change the button text to 'HIDE COMMENTS'
commentButton . textContent = 'HIDE COMMENTS' ;
2024-12-19 03:39:15 +00:00
} else {
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
// Hide comments
2024-12-19 03:39:15 +00:00
commentsSection . style . display = 'none' ;
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
commentButton . textContent = ` COMMENTS ( ${ count } ) ` ;
2024-12-19 03:39:15 +00:00
}
} ;
const createLinkDisplayModal = 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;" >
< div style = "position: relative; margin: 10% auto; width: 95%; height: 80%; background: white; border-radius: 10px; overflow: hidden;" >
< iframe id = "modalContent" src = "" style = "width: 100%; height: 100%; border: none;" > < / i f r a m e >
< button onclick = "closeLinkDisplayModal()" 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 openLinkDisplayModal = async ( link ) => {
const processedLink = await processQortalLinkForRendering ( 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 closeLinkDisplayModal = async ( ) => {
const modal = document . getElementById ( 'modal' ) ;
const modalContent = document . getElementById ( 'modalContent' ) ;
modal . style . display = 'none' ; // Hide the modal
modalContent . src = '' ; // Clear the iframe source
}
2024-12-21 06:07:18 +00:00
// const processQortalLinkForRendering = async (link) => {
// if (link.startsWith('qortal://')) {
// 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
// return `/render/${firstParam}${remainingPath}`;
// }
// }
// return link; // Return unchanged if not a Qortal link
// }
2024-12-19 03:39:15 +00:00
const processQortalLinkForRendering = async ( link ) => {
if ( link . startsWith ( 'qortal://' ) ) {
const match = link . match ( /^qortal:\/\/([^/]+)(\/.*)?$/ ) ;
if ( match ) {
2024-12-21 06:07:18 +00:00
const firstParam = match [ 1 ] . toUpperCase ( ) ;
const remainingPath = match [ 2 ] || "" ;
const themeColor = window . _qdnTheme || 'default' ; // Fallback to 'default' if undefined
// Simulating async operation if needed
await new Promise ( resolve => setTimeout ( resolve , 10 ) ) ;
// Append theme as a query parameter
return ` /render/ ${ firstParam } ${ remainingPath } ?theme= ${ themeColor } ` ;
}
}
return link ;
} ;
async function getMinterAvatar ( minterName ) {
const avatarUrl = ` /arbitrary/THUMBNAIL/ ${ minterName } /qortal_avatar ` ;
try {
const response = await fetch ( avatarUrl , { method : 'HEAD' } ) ;
if ( response . ok ) {
// Avatar exists, return the image HTML
return ` <img src=" ${ avatarUrl } " alt="User Avatar" class="user-avatar" style="width: 50px; height: 50px; border-radius: 50%; align-self: center;"> ` ;
} else {
// Avatar not found or no permission
return '' ;
2024-12-19 03:39:15 +00:00
}
2024-12-21 06:07:18 +00:00
} catch ( error ) {
console . error ( 'Error checking avatar availability:' , error ) ;
return '' ;
2024-12-19 03:39:15 +00:00
}
}
// Create the overall Minter Card HTML -----------------------------------------------
2024-12-20 05:28:36 +00:00
const createEncryptedCardHTML = async ( cardData , pollResults , cardIdentifier , commentCount ) => {
2024-12-24 08:27:17 +00:00
const { minterName , header , content , links , creator , timestamp , poll , topicMode } = cardData
const formattedDate = new Date ( timestamp ) . toLocaleString ( )
const minterAvatar = ! topicMode ? await getMinterAvatar ( minterName ) : null
2024-12-21 06:07:18 +00:00
// const creatorAvatar = `/arbitrary/THUMBNAIL/${creator}/qortal_avatar`;
const creatorAvatar = await getMinterAvatar ( creator )
2024-12-19 03:39:15 +00:00
const linksHTML = links . map ( ( link , index ) => `
< button onclick = "openLinkDisplayModal('${link}')" >
$ { ` Link ${ index + 1 } - ${ link } ` }
< / b u t t o n >
2024-12-24 08:27:17 +00:00
` ).join("")
const isUndefinedUser = ( minterName === 'undefined' )
2024-12-19 03:39:15 +00:00
2024-12-24 08:27:17 +00:00
const hasTopicMode = Object . prototype . hasOwnProperty . call ( cardData , 'topicMode' )
// 2) Decide if this card is showing as "Topic" or "Name"
let showTopic = false
if ( hasTopicMode ) {
// If present, see if it's actually "true" or true
const modeVal = cardData . topicMode ;
showTopic = ( modeVal === true || modeVal === 'true' )
} else {
if ( ! isUndefinedUser ) {
// No topicMode => older card => default to Name
showTopic = false
}
}
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
2024-12-24 08:27:17 +00:00
const cardColorCode = showTopic ? '#0e1b15' : '#151f28'
const minterOrTopicHtml = ( ( showTopic ) || ( isUndefinedUser ) ) ? `
< div class = "support-header" > < h5 > REGARDING ( Topic ) : < / h 5 > < / d i v >
< h3 > $ { minterName } < / h 3 > ` :
`
< div class = "support-header" > < h5 > REGARDING ( Name ) : < / h 5 > < / d i v >
$ { minterAvatar }
< h3 > $ { minterName } < / h 3 > `
const minterGroupMembers = await fetchMinterGroupMembers ( )
const minterAdmins = await fetchMinterGroupAdmins ( )
2024-12-19 03:39:15 +00:00
const { adminYes = 0 , adminNo = 0 , minterYes = 0 , minterNo = 0 , totalYes = 0 , totalNo = 0 , totalYesWeight = 0 , totalNoWeight = 0 } = await calculateAdminBoardPollResults ( pollResults , minterGroupMembers , minterAdmins )
await createModal ( )
return `
2024-12-24 08:27:17 +00:00
< div class = "admin-card" style = "background-color: ${cardColorCode}" >
2024-12-19 03:39:15 +00:00
< div class = "minter-card-header" >
2024-12-21 06:07:18 +00:00
< h2 class = "support-header" > Created By : < / h 2 >
$ { creatorAvatar }
2024-12-19 03:39:15 +00:00
< h2 > $ { creator } < / h 2 >
2024-12-24 08:27:17 +00:00
$ { minterOrTopicHtml }
2024-12-19 03:39:15 +00:00
< p > $ { header } < / p >
< / d i v >
< div class = "info" >
$ { content }
< / d i v >
2024-12-21 06:07:18 +00:00
< div class = "support-header" > < h5 > LINKS < / h 5 > < / d i v >
2024-12-19 03:39:15 +00:00
< div class = "info-links" >
$ { linksHTML }
< / d i v >
2024-12-21 06:07:18 +00:00
< div class = "results-header support-header" > < h5 > CURRENT RESULTS < / h 5 > < / d i v >
2024-12-19 03:39:15 +00:00
< div class = "minter-card-results" >
< div class = "admin-results" >
2024-12-21 06:07:18 +00:00
< span class = "admin-yes" > Admin Support : $ { adminYes } < / s p a n >
< span class = "admin-no" > Admin Against : $ { adminNo } < / s p a n >
2024-12-19 03:39:15 +00:00
< / d i v >
< div class = "minter-results" >
2024-12-21 06:07:18 +00:00
< span class = "minter-yes" > Supporting Weight $ { totalYesWeight } < / s p a n >
< span class = "minter-no" > Denial Weight $ { totalNoWeight } < / s p a n >
2024-12-19 03:39:15 +00:00
< / d i v >
< / d i v >
2024-12-24 08:27:17 +00:00
< div class = "support-header" > < h5 > ACTIONS FOR < / h 5 > < h 5 s t y l e = " c o l o r : # f f a e 4 2 ; " > $ { m i n t e r N a m e } < / h 5 >
2024-12-21 06:07:18 +00:00
< p style = "color: #c7c7c7; font-size: .65rem; margin-top: 1vh" > ( click COMMENTS button to open / close card comments ) < / p >
< / d i v >
2024-12-19 03:39:15 +00:00
< div class = "actions" >
< div class = "actions-buttons" >
2024-12-24 08:27:17 +00:00
< button class = "yes" onclick = "voteYesOnPoll('${poll}')" > YES < / b u t t o n >
This version includes many changes and performance improvements. Further performance improvements will be coming soon. This change includes a cache for the published data on the forum. Messages of up to 2000 in number, will be stored locally in browser storage, that way if the message has already been loaded by that computer, it will not have to pull the data again from QDN. It will be stored in encrypted format for the Admin room. This same caching will be applied to the Minter and Admin boards in the future. Also, reply issues that were present before should be resolved, all replies, regardless of when they were published, will now show their previews in the message pane as they are supposed to. Previously if a reply was on another page, it would not load this preview. The encrypted portions of the app now include a method of caching the admin public keys, for faster publishing. The Minter and Admin boards have a new comment loading display when the comments button is clicked to let users know that data is being loaded, on top of the existing comment count. Other new features and additional performance improvements are in planning. Also, the issue preventing comments from those that had not already loaded the forum, in the Admin Board, has been resolved as well.
2024-12-27 04:06:51 +00:00
< button id = "comment-button-${cardIdentifier}" data - comment - count = "${commentCount}" class = "comment" onclick = "toggleEncryptedComments('${cardIdentifier}')" > COMMENTS ( $ { commentCount } ) < / b u t t o n >
2024-12-24 08:27:17 +00:00
< button class = "no" onclick = "voteNoOnPoll('${poll}')" > NO < / b u t t o n >
2024-12-19 03:39:15 +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 >
2024-12-21 06:07:18 +00:00
< textarea id = "new-comment-${cardIdentifier}" placeholder = "Input your comment..." style = "width: 100%; margin-top: 10px;" > < / t e x t a r e a >
2024-12-19 03:39:15 +00:00
< button onclick = "postEncryptedComment('${cardIdentifier}')" > Post Comment < / b u t t o n >
< / d i v >
2024-12-21 06:07:18 +00:00
< p style = "font-size: 0.75rem; margin-top: 1vh; color: #4496a1" > By : $ { creator } - $ { formattedDate } < / p >
2024-12-19 03:39:15 +00:00
< / d i v >
2024-12-24 08:27:17 +00:00
`
2024-12-19 03:39:15 +00:00
}