2024-12-11 14:40:32 -08:00
// Set the forumAdminGroups variable
2024-12-27 23:04:16 -08:00
let adminGroups = [ "Q-Mintership-admin" , "dev-group" , "Mintership-Forum-Admins" ]
let adminGroupIDs = [ "721" , "1" , "673" ]
2024-12-11 14:40:32 -08:00
// Settings to allow non-devmode development with 'live-server' module
2024-12-27 23:04:16 -08:00
let baseUrl = ''
let isOutsideOfUiDevelopment = false
2024-12-11 14:40:32 -08:00
if ( typeof qortalRequest === 'function' ) {
2024-12-27 23:04:16 -08:00
console . log ( 'qortalRequest is available as a function. Setting development mode to false and baseUrl to nothing.' )
isOutsideOfUiDevelopment = false
baseUrl = ''
2024-12-11 14:40:32 -08:00
} else {
2024-12-27 23:04:16 -08:00
console . log ( 'qortalRequest is not available as a function. Setting baseUrl to localhost.' )
isOutsideOfUiDevelopment = true
baseUrl = "http://localhost:12391"
}
2024-12-11 14:40:32 -08:00
// USEFUL UTILITIES ----- ----- -----
// Generate a short random ID to utilize at the end of unique identifiers.
const uid = async ( ) => {
2024-12-27 23:04:16 -08:00
console . log ( 'uid function called' )
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
let result = ''
const charactersLength = characters . length
2024-12-11 14:40:32 -08:00
for ( let i = 0 ; i < 6 ; i ++ ) {
2024-12-27 23:04:16 -08:00
result += characters . charAt ( Math . floor ( Math . random ( ) * charactersLength ) )
}
console . log ( 'Generated uid:' , result )
return result
}
2024-12-20 22:07:18 -08:00
// a non-async version of the uid function, in case non-async functions need it. Ultimately we can probably remove uid but need to ensure no apps are using it asynchronously first. so this is kept for that purpose for now.
const randomID = ( ) => {
2024-12-27 23:04:16 -08:00
console . log ( 'randomID non-async' )
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
let result = ''
const charactersLength = characters . length
2024-12-20 22:07:18 -08:00
for ( let i = 0 ; i < 6 ; i ++ ) {
2024-12-27 23:04:16 -08:00
result += characters . charAt ( Math . floor ( Math . random ( ) * charactersLength ) )
}
console . log ( 'Generated uid:' , result )
return result
2024-12-20 22:07:18 -08:00
}
2024-12-11 14:40:32 -08:00
// Turn a unix timestamp into a human-readable date
const timestampToHumanReadableDate = async ( timestamp ) => {
2024-12-27 23:04:16 -08:00
const date = new Date ( timestamp )
const day = date . getDate ( )
const month = date . getMonth ( ) + 1
const year = date . getFullYear ( ) - 2000
const hours = date . getHours ( )
const minutes = date . getMinutes ( )
const seconds = date . getSeconds ( )
const formattedDate = ` ${ month } . ${ day } . ${ year } @ ${ hours } : ${ minutes } : ${ seconds } `
console . log ( 'Formatted date:' , formattedDate )
return formattedDate
}
2024-12-11 14:40:32 -08:00
// Base64 encode a string
const base64EncodeString = async ( str ) => {
2024-12-27 23:04:16 -08:00
const encodedString = btoa ( String . fromCharCode . apply ( null , new Uint8Array ( new TextEncoder ( ) . encode ( str ) . buffer ) ) )
console . log ( 'Encoded string:' , encodedString )
return encodedString
}
2024-12-11 14:40:32 -08:00
2024-12-18 17:33:09 -08:00
// const decryptToUnit8ArraySubject =
2024-12-27 23:04:16 -08:00
// base64ToUint8Array(decryptedData)
2024-12-18 17:33:09 -08:00
// const responseData = uint8ArrayToObject(
// decryptToUnit8ArraySubject
2024-12-27 23:04:16 -08:00
// )
2024-12-18 17:33:09 -08:00
const base64ToUint8Array = async ( base64 ) => {
const binaryString = atob ( base64 )
const len = binaryString . length
const bytes = new Uint8Array ( len )
for ( let i = 0 ; i < len ; i ++ ) {
bytes [ i ] = binaryString . charCodeAt ( i )
}
return bytes
}
const uint8ArrayToObject = async ( uint8Array ) => {
// Decode the byte array using TextDecoder
const decoder = new TextDecoder ( )
const jsonString = decoder . decode ( uint8Array )
// Convert the JSON string back into an object
const obj = JSON . parse ( jsonString )
return obj
}
2024-12-11 14:40:32 -08:00
const objectToBase64 = async ( obj ) => {
// Step 1: Convert the object to a JSON string
2024-12-27 23:04:16 -08:00
const jsonString = JSON . stringify ( obj )
2024-12-11 14:40:32 -08:00
// Step 2: Create a Blob from the JSON string
2024-12-27 23:04:16 -08:00
const blob = new Blob ( [ jsonString ] , { type : 'application/json' } )
2024-12-11 14:40:32 -08:00
// Step 3: Create a FileReader to read the Blob as a base64-encoded string
return new Promise ( ( resolve , reject ) => {
2024-12-27 23:04:16 -08:00
const reader = new FileReader ( )
2024-12-11 14:40:32 -08:00
reader . onloadend = ( ) => {
if ( typeof reader . result === 'string' ) {
2024-12-27 23:04:16 -08:00
// Remove 'data:application/jsonbase64,' prefix
2024-12-28 10:03:27 -08:00
const base64 = reader . result . replace ( 'data:application/json;base64,' , '' )
2024-12-27 23:04:16 -08:00
console . log ( ` base64 resolution: ${ base64 } ` )
resolve ( base64 )
2024-12-11 14:40:32 -08:00
} else {
2024-12-27 23:04:16 -08:00
reject ( new Error ( 'Failed to read the Blob as a base64-encoded string' ) )
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
reader . onerror = ( ) => {
2024-12-27 23:04:16 -08:00
reject ( reader . error )
}
reader . readAsDataURL ( blob )
} )
}
2024-12-11 14:40:32 -08:00
// User state util
const userState = {
isLoggedIn : false ,
accountName : null ,
accountAddress : null ,
isAdmin : false ,
isMinterAdmin : false ,
isForumAdmin : false
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
// USER-RELATED QORTAL CALLS ------------------------------------------
// Obtain the address of the authenticated user checking userState.accountAddress first.
const getUserAddress = async ( ) => {
try {
if ( userState . accountAddress ) {
2024-12-27 23:04:16 -08:00
console . log ( 'User address found in state:' , userState . accountAddress )
return userState . accountAddress
}
const userAccount = await qortalRequest ( { action : "GET_USER_ACCOUNT" } )
2024-12-11 14:40:32 -08:00
if ( userAccount ) {
2024-12-27 23:04:16 -08:00
console . log ( 'Account address:' , userAccount . address )
userState . accountAddress = userAccount . address
console . log ( 'Account address added to state:' , userState . accountAddress )
return userState . accountAddress
}
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error fetching account address:' , error )
throw error
}
}
2024-12-11 14:40:32 -08:00
const fetchOwnerAddressFromName = async ( name ) => {
2024-12-27 23:04:16 -08:00
console . log ( 'fetchOwnerAddressFromName called' )
console . log ( 'name:' , name )
2024-12-11 14:40:32 -08:00
try {
const response = await fetch ( ` ${ baseUrl } /names/ ${ name } ` , {
headers : { 'Accept' : 'application/json' } ,
method : 'GET' ,
2024-12-27 23:04:16 -08:00
} )
const data = await response . json ( )
console . log ( 'Fetched owner address:' , data . owner )
return data . owner
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error fetching owner address:' , error )
return null
}
}
2024-12-11 14:40:32 -08:00
const verifyUserIsAdmin = async ( ) => {
2024-12-27 23:04:16 -08:00
console . log ( 'verifyUserIsAdmin called (QortalApi.js)' )
2024-12-11 14:40:32 -08:00
try {
2024-12-27 23:04:16 -08:00
const accountAddress = userState . accountAddress || await getUserAddress ( )
userState . accountAddress = accountAddress
2024-12-12 17:23:34 -08:00
2024-12-27 23:04:16 -08:00
const userGroups = await getUserGroups ( accountAddress )
console . log ( 'userGroups:' , userGroups )
2024-12-12 17:23:34 -08:00
2024-12-27 23:04:16 -08:00
const minterGroupAdmins = await fetchMinterGroupAdmins ( )
console . log ( 'minterGroupAdmins.members:' , minterGroupAdmins )
2024-12-12 17:23:34 -08:00
if ( ! Array . isArray ( userGroups ) ) {
2024-12-27 23:04:16 -08:00
throw new Error ( 'userGroups is not an array or is undefined' )
2024-12-12 17:23:34 -08:00
}
if ( ! Array . isArray ( minterGroupAdmins ) ) {
2024-12-27 23:04:16 -08:00
throw new Error ( 'minterGroupAdmins.members is not an array or is undefined' )
2024-12-12 17:23:34 -08:00
}
2024-12-27 23:04:16 -08:00
const isAdmin = userGroups . some ( group => adminGroups . includes ( group . groupName ) )
const isMinterAdmin = minterGroupAdmins . some ( admin => admin . member === userState . accountAddress && admin . isAdmin )
2024-12-12 17:23:34 -08:00
2024-12-11 14:40:32 -08:00
if ( isMinterAdmin ) {
2024-12-27 23:04:16 -08:00
userState . isMinterAdmin = true
2024-12-11 14:40:32 -08:00
}
if ( isAdmin ) {
2024-12-27 23:04:16 -08:00
userState . isAdmin = true
userState . isForumAdmin = true
2024-12-12 17:23:34 -08:00
}
2024-12-27 23:04:16 -08:00
return userState . isAdmin
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error verifying user admin status:' , error )
throw error
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
2024-12-12 17:23:34 -08:00
2024-12-11 14:40:32 -08:00
const verifyAddressIsAdmin = async ( address ) => {
2024-12-27 23:04:16 -08:00
console . log ( 'verifyAddressIsAdmin called' )
console . log ( 'address:' , address )
2024-12-11 14:40:32 -08:00
try {
if ( ! address ) {
2024-12-27 23:04:16 -08:00
console . log ( 'No address provided' )
return false
}
const userGroups = await getUserGroups ( address )
const minterGroupAdmins = await fetchMinterGroupAdmins ( )
2024-12-11 14:40:32 -08:00
const isAdmin = await userGroups . some ( group => adminGroups . includes ( group . groupName ) )
const isMinterAdmin = minterGroupAdmins . members . some ( admin => admin . member === address && admin . isAdmin )
if ( ( isMinterAdmin ) || ( isAdmin ) ) {
2024-12-27 23:04:16 -08:00
return true
2024-12-11 14:40:32 -08:00
} else {
return false
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error verifying address admin status:' , error )
2024-12-11 14:40:32 -08:00
throw error
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
const getNameInfo = async ( name ) => {
2024-12-27 23:04:16 -08:00
console . log ( 'getNameInfo called' )
console . log ( 'name:' , name )
2024-12-11 14:40:32 -08:00
try {
2024-12-27 23:04:16 -08:00
const response = await fetch ( ` ${ baseUrl } /names/ ${ name } ` )
const data = await response . json ( )
console . log ( 'Fetched name info:' , data )
2024-12-11 14:40:32 -08:00
return {
name : data . name ,
reducedName : data . reducedName ,
owner : data . owner ,
data : data . data ,
registered : data . registered ,
updated : data . updated ,
isForSale : data . isForSale ,
salePrice : data . salePrice
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . log ( 'Error fetching name info:' , error )
return null
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
const getPublicKeyByName = async ( name ) => {
2024-12-27 23:04:16 -08:00
2024-12-11 14:40:32 -08:00
try {
2024-12-27 23:04:16 -08:00
const nameInfo = await getNameInfo ( name )
const address = nameInfo . owner
const publicKey = await getPublicKeyFromAddress ( address )
console . log ( ` Found public key: for name: ${ name } ` , publicKey )
return publicKey
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . log ( 'Error obtaining public key from name:' , error )
return null
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
const getPublicKeyFromAddress = async ( address ) => {
try {
const response = await fetch ( ` ${ baseUrl } /addresses/ ${ address } ` , {
method : 'GET' ,
headers : { 'Accept' : 'application/json' }
2024-12-27 23:04:16 -08:00
} )
const data = await response . json ( )
const publicKey = data . publicKey
return publicKey
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . log ( 'Error fetching public key from address:' , error )
return null
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
const getAddressFromPublicKey = async ( publicKey ) => {
2024-12-27 23:04:16 -08:00
2024-12-11 14:40:32 -08:00
try {
const response = await fetch ( ` ${ baseUrl } /addresses/convert/ ${ publicKey } ` , {
method : 'GET' ,
headers : { 'Accept' : 'text/plain' }
2024-12-27 23:04:16 -08:00
} )
const address = await response . text ( )
return address
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . log ( 'Error converting public key to address:' , error )
return null
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
const login = async ( ) => {
2024-12-27 23:04:16 -08:00
2024-12-11 14:40:32 -08:00
try {
if ( userState . accountName && ( userState . isAdmin || userState . isLoggedIn ) && userState . accountAddress ) {
2024-12-27 23:04:16 -08:00
console . log ( ` Account name found in userState: ' ${ userState . accountName } ', no need to call API...skipping API call. ` )
return userState . accountName
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
const accountAddress = userState . accountAddress || await getUserAddress ( )
2024-12-11 14:40:32 -08:00
const accountNames = await qortalRequest ( {
action : "GET_ACCOUNT_NAMES" ,
address : accountAddress ,
2024-12-27 23:04:16 -08:00
} )
2024-12-11 14:40:32 -08:00
if ( accountNames ) {
2024-12-27 23:04:16 -08:00
userState . isLoggedIn = true
userState . accountName = accountNames [ 0 ] . name
userState . accountAddress = accountAddress
console . log ( 'User has been logged in successfully!' )
return userState . accountName
2024-12-11 14:40:32 -08:00
} else {
2024-12-27 23:04:16 -08:00
throw new Error ( "No account names found. Are you logged in? Do you have a registered name?" )
2024-12-11 14:40:32 -08:00
}
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error fetching account names:' , error )
throw error
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
const getNamesFromAddress = async ( address ) => {
try {
const response = await fetch ( ` ${ baseUrl } /names/address/ ${ address } ?limit=20 ` , {
method : 'GET' ,
headers : { 'Accept' : 'application/json' }
2024-12-27 23:04:16 -08:00
} )
const names = await response . json ( )
return names . length > 0 ? names [ 0 ] . name : address // Return name if found, else return address
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( ` Error fetching names for address ${ address } : ` , error )
return address
}
2024-12-11 14:40:32 -08:00
}
2024-12-12 17:23:34 -08:00
// QORTAL GROUP-RELATED CALLS ------------------------------------------------------------------------------------
2024-12-11 14:40:32 -08:00
const getUserGroups = async ( userAddress ) => {
2024-12-27 23:04:16 -08:00
2024-12-11 14:40:32 -08:00
try {
if ( ! userAddress && userState . accountAddress ) {
2024-12-27 23:04:16 -08:00
userAddress = userState . accountAddress
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
2024-12-11 14:40:32 -08:00
const response = await fetch ( ` ${ baseUrl } /groups/member/ ${ userAddress } ` , {
method : 'GET' ,
headers : { 'accept' : 'application/json' }
2024-12-27 23:04:16 -08:00
} )
const data = await response . json ( )
return data
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error fetching user groups:' , error )
throw error
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
const fetchMinterGroupAdmins = async ( ) => {
2024-12-27 23:04:16 -08:00
2024-12-11 14:40:32 -08:00
const response = await fetch ( ` ${ baseUrl } /groups/members/694?onlyAdmins=true&limit=0&reverse=true ` , {
method : 'GET' ,
headers : { 'Accept' : 'application/json' }
2024-12-27 23:04:16 -08:00
} )
const admins = await response . json ( )
2024-12-12 17:23:34 -08:00
if ( ! Array . isArray ( admins . members ) ) {
2024-12-27 23:04:16 -08:00
throw new Error ( "Expected 'members' to be an array but got a different structure" )
2024-12-12 17:23:34 -08:00
}
const adminMembers = admins . members
2024-12-27 23:04:16 -08:00
return adminMembers
2024-12-12 17:23:34 -08:00
//use what is returned .member to obtain each member... {"member": "memberAddress", "isAdmin": "true"}
2024-12-11 14:40:32 -08:00
}
2024-12-14 19:40:31 -08:00
const fetchAllAdminGroupsMembers = async ( ) => {
try {
2024-12-27 23:04:16 -08:00
let adminGroupMemberAddresses = [ ] // Declare outside loop to accumulate results
2024-12-14 19:40:31 -08:00
for ( const groupID of adminGroupIDs ) {
const response = await fetch ( ` ${ baseUrl } /groups/members/ ${ groupID } ?limit=0 ` , {
method : 'GET' ,
headers : { 'Accept' : 'application/json' } ,
2024-12-27 23:04:16 -08:00
} )
2024-12-14 19:40:31 -08:00
2024-12-27 23:04:16 -08:00
const groupData = await response . json ( )
2024-12-14 19:40:31 -08:00
if ( groupData . members && Array . isArray ( groupData . members ) ) {
2024-12-27 23:04:16 -08:00
adminGroupMemberAddresses . push ( ... groupData . members ) // Merge members into the array
2024-12-14 19:40:31 -08:00
} else {
2024-12-27 23:04:16 -08:00
console . warn ( ` Group ${ groupID } did not return valid members. ` )
2024-12-14 19:40:31 -08:00
}
}
2024-12-27 23:04:16 -08:00
return adminGroupMemberAddresses
2024-12-14 19:40:31 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . log ( 'Error fetching admin group members' , error )
2024-12-14 19:40:31 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-14 19:40:31 -08:00
2024-12-11 14:40:32 -08:00
const fetchMinterGroupMembers = async ( ) => {
try {
const response = await fetch ( ` ${ baseUrl } /groups/members/694?limit=0 ` , {
method : 'GET' ,
headers : { 'Accept' : 'application/json' } ,
2024-12-27 23:04:16 -08:00
} )
2024-12-11 14:40:32 -08:00
if ( ! response . ok ) {
2024-12-27 23:04:16 -08:00
throw new Error ( ` HTTP error! Status: ${ response . status } ` )
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
const data = await response . json ( )
2024-12-11 14:40:32 -08:00
if ( ! Array . isArray ( data . members ) ) {
2024-12-27 23:04:16 -08:00
throw new Error ( "Expected 'members' to be an array but got a different structure" )
2024-12-11 14:40:32 -08:00
}
2024-12-12 17:23:34 -08:00
2024-12-27 23:04:16 -08:00
return data . members
2024-12-12 17:23:34 -08:00
//use what is returned .member to obtain each member... {"member": "memberAddress", "joined": "{timestamp}"}
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( "Error fetching minter group members:" , error )
return [ ] // Return an empty array to prevent further errors
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
const fetchAllGroups = async ( limit ) => {
if ( ! limit ) {
2024-12-27 23:04:16 -08:00
limit = 2000
2024-12-11 14:40:32 -08:00
}
try {
2024-12-27 23:04:16 -08:00
const response = await fetch ( ` ${ baseUrl } /groups?limit= ${ limit } &reverse=true ` )
const data = await response . json ( )
return data
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error fetching all groups:' , error )
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
2024-12-14 19:40:31 -08:00
const fetchAdminGroupsMembersPublicKeys = async ( ) => {
try {
2024-12-27 23:04:16 -08:00
let adminGroupMemberAddresses = [ ] // Declare outside loop to accumulate results
2024-12-14 19:40:31 -08:00
for ( const groupID of adminGroupIDs ) {
const response = await fetch ( ` ${ baseUrl } /groups/members/ ${ groupID } ?limit=0 ` , {
method : 'GET' ,
headers : { 'Accept' : 'application/json' } ,
2024-12-27 23:04:16 -08:00
} )
2024-12-14 19:40:31 -08:00
2024-12-27 23:04:16 -08:00
const groupData = await response . json ( )
2024-12-14 19:40:31 -08:00
if ( groupData . members && Array . isArray ( groupData . members ) ) {
2024-12-27 23:04:16 -08:00
adminGroupMemberAddresses . push ( ... groupData . members ) // Merge members into the array
2024-12-14 19:40:31 -08:00
} else {
2024-12-27 23:04:16 -08:00
console . warn ( ` Group ${ groupID } did not return valid members. ` )
2024-12-14 19:40:31 -08:00
}
}
// Check if adminGroupMemberAddresses has valid data
if ( ! Array . isArray ( adminGroupMemberAddresses ) ) {
2024-12-27 23:04:16 -08:00
throw new Error ( "Expected 'adminGroupMemberAddresses' to be an array but got a different structure" )
2024-12-14 19:40:31 -08:00
}
2024-12-27 23:04:16 -08:00
let allMemberPublicKeys = [ ] // Declare outside loop to accumulate results
2024-12-14 19:40:31 -08:00
for ( const member of adminGroupMemberAddresses ) {
2024-12-27 23:04:16 -08:00
const memberPublicKey = await getPublicKeyFromAddress ( member . member )
allMemberPublicKeys . push ( memberPublicKey )
2024-12-14 19:40:31 -08:00
}
// Check if allMemberPublicKeys has valid data
if ( ! Array . isArray ( allMemberPublicKeys ) ) {
2024-12-27 23:04:16 -08:00
throw new Error ( "Expected 'allMemberPublicKeys' to be an array but got a different structure" )
2024-12-14 19:40:31 -08:00
}
2024-12-27 23:04:16 -08:00
console . log ( ` AdminGroupMemberPublicKeys have been fetched. ` )
return allMemberPublicKeys
2024-12-14 19:40:31 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error fetching admin group member public keys:' , error )
return [ ] // Return an empty array to prevent further errors
2024-12-14 19:40:31 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-14 19:40:31 -08:00
2024-12-17 22:24:40 -08:00
// QDN data calls --------------------------------------------------------------------------------------------------
2024-12-11 14:40:32 -08:00
const searchLatestDataByIdentifier = async ( identifier ) => {
try {
const response = await fetch ( ` ${ baseUrl } /arbitrary/resources/search?service=DOCUMENT&identifier= ${ identifier } &includestatus=true&mode=ALL&limit=0&reverse=true ` , {
method : 'GET' ,
headers : { 'Accept' : 'application/json' }
2024-12-27 23:04:16 -08:00
} )
const latestData = await response . json ( )
return latestData
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error fetching latest published data:' , error )
return null
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
const publishMultipleResources = async ( resources , publicKeys = null , isPrivate = false ) => {
const request = {
action : 'PUBLISH_MULTIPLE_QDN_RESOURCES' ,
resources : resources ,
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
if ( isPrivate && publicKeys ) {
2024-12-27 23:04:16 -08:00
request . encrypt = true
request . publicKeys = publicKeys
}
2024-12-11 14:40:32 -08:00
try {
2024-12-27 23:04:16 -08:00
const response = await qortalRequest ( request )
console . log ( 'Multiple resources published successfully:' , response )
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error publishing multiple resources:' , error )
}
}
2024-12-11 14:40:32 -08:00
2024-12-27 23:04:16 -08:00
// NOTE - the object must be in base64 when sent
2024-12-17 22:24:40 -08:00
const decryptObject = async ( encryptedData ) => {
const response = await qortalRequest ( {
action : 'DECRYPT_DATA' ,
encryptedData , // has to be in base64 format
// publicKey: publisherPublicKey // requires the public key of the opposite user with whom you've created the encrypted data. For DIRECT messages only.
2024-12-27 23:04:16 -08:00
} )
2024-12-17 22:24:40 -08:00
const decryptedObject = response
return decryptedObject
}
2024-12-18 17:33:09 -08:00
const decryptAndParseObject = async ( base64Data ) => {
const decrypto = await decryptObject ( base64Data )
const binaryString = atob ( decrypto )
const len = binaryString . length
const bytes = new Uint8Array ( len )
for ( let i = 0 ; i < len ; i ++ ) {
bytes [ i ] = binaryString . charCodeAt ( i )
}
// Decode the byte array using TextDecoder
const decoder = new TextDecoder ( )
const jsonString = decoder . decode ( bytes )
// Convert the JSON string back into an object
const obj = JSON . parse ( jsonString )
return obj
}
2024-12-17 22:24:40 -08:00
2024-12-11 14:40:32 -08:00
const searchResourcesWithMetadata = async ( query , limit ) => {
try {
if ( limit == 0 ) {
2024-12-27 23:04:16 -08:00
limit = 0
2024-12-11 14:40:32 -08:00
} else if ( ! limit || ( limit < 10 && limit != 0 ) ) {
2024-12-27 23:04:16 -08:00
limit = 200
2024-12-11 14:40:32 -08:00
}
const response = await fetch ( ` ${ baseUrl } /arbitrary/resources/search?query= ${ query } &mode=ALL&includestatus=true&includemetadata=true&limit= ${ limit } &reverse=true ` , {
method : 'GET' ,
headers : { 'accept' : 'application/json' }
2024-12-27 23:04:16 -08:00
} )
const data = await response . json ( )
console . log ( 'Search results with metadata:' , data )
return data
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error searching for resources with metadata:' , error )
throw error
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
const searchAllResources = async ( query , limit , after , reverse = false ) => {
2024-12-27 23:04:16 -08:00
console . log ( 'searchAllResources called. Query:' , query , 'Limit:' , limit , 'Reverse:' , reverse )
2024-12-11 14:40:32 -08:00
try {
if ( limit == 0 ) {
2024-12-27 23:04:16 -08:00
limit = 0
2024-12-11 14:40:32 -08:00
}
if ( ! limit || ( limit < 10 && limit != 0 ) ) {
2024-12-27 23:04:16 -08:00
limit = 200
2024-12-11 14:40:32 -08:00
}
if ( after == null || after === undefined ) {
2024-12-27 23:04:16 -08:00
after = 0
2024-12-11 14:40:32 -08:00
}
const response = await fetch ( ` ${ baseUrl } /arbitrary/resources/search?query= ${ query } &mode=ALL&after= ${ after } &includestatus=false&includemetadata=false&limit= ${ limit } &reverse= ${ reverse } ` , {
method : 'GET' ,
headers : { 'accept' : 'application/json' }
2024-12-27 23:04:16 -08:00
} )
const data = await response . json ( )
console . log ( 'Search results with metadata:' , data )
return data
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error searching for resources with metadata:' , error )
throw error
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
2024-12-14 19:40:31 -08:00
const searchAllWithOffset = async ( service , query , limit , offset , room ) => {
2024-12-11 14:40:32 -08:00
try {
2024-12-14 19:40:31 -08:00
if ( ! service || ( service === "BLOG_POST" && room !== "admins" ) ) {
2024-12-27 23:04:16 -08:00
console . log ( "Performing search for BLOG_POST..." )
2024-12-14 19:40:31 -08:00
const response = await qortalRequest ( {
action : "SEARCH_QDN_RESOURCES" ,
service : "BLOG_POST" ,
query ,
limit ,
offset ,
mode : "ALL" ,
reverse : false ,
2024-12-27 23:04:16 -08:00
} )
return response
2024-12-14 19:40:31 -08:00
}
if ( room === "admins" ) {
2024-12-27 23:04:16 -08:00
service = service || "MAIL_PRIVATE" // Default to MAIL_PRIVATE if no service provided
console . log ( "Performing search for MAIL_PRIVATE in Admin room..." )
2024-12-14 19:40:31 -08:00
const response = await qortalRequest ( {
action : "SEARCH_QDN_RESOURCES" ,
service ,
query ,
limit ,
offset ,
mode : "ALL" ,
reverse : false ,
2024-12-27 23:04:16 -08:00
} )
return response
2024-12-14 19:40:31 -08:00
}
2024-12-27 23:04:16 -08:00
console . warn ( "Invalid parameters passed to searchAllWithOffset" )
return [ ] // Return empty array if no valid conditions match
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( "Error during SEARCH_QDN_RESOURCES:" , error )
return [ ] // Return empty array on error
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08: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-26 20:06:51 -08:00
// NOTE - This function does a search and will return EITHER AN ARRAY OR A SINGLE OBJECT. if you want to guarantee a single object, pass 1 as limit. i.e. await searchSimple(service, identifier, "", 1) will return a single object.
2024-12-27 23:04:16 -08:00
const searchSimple = async ( service , identifier , name , limit = 1500 , offset = 0 , room = '' , reverse = 'true' ) => {
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-26 20:06:51 -08:00
try {
2024-12-27 23:04:16 -08:00
let urlSuffix = ` service= ${ service } &identifier= ${ identifier } &name= ${ name } &prefix=true&limit= ${ limit } &offset= ${ offset } &reverse= ${ reverse } `
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-26 20:06:51 -08:00
2024-12-27 23:04:16 -08:00
if ( name && ! identifier && ! room ) {
console . log ( 'name only searchSimple' , name )
urlSuffix = ` service= ${ service } &name= ${ name } &limit= ${ limit } &prefix=true&reverse= ${ reverse } `
} else if ( ! name && identifier && ! room ) {
console . log ( 'identifier only searchSimple' , identifier )
urlSuffix = ` service= ${ service } &identifier= ${ identifier } &limit= ${ limit } &prefix=true&reverse= ${ reverse } `
} else if ( ! name && ! identifier && ! room ) {
console . error ( ` name: ${ name } AND identifier: ${ identifier } not passed. Must include at least one... ` )
return null
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-26 20:06:51 -08:00
} else {
2024-12-27 23:04:16 -08:00
console . log ( ` final searchSimple params = service: ' ${ service } ', identifier: ' ${ identifier } ', name: ' ${ name } ', limit: ' ${ limit } ', offset: ' ${ offset } ', room: ' ${ room } ', reverse: ' ${ reverse } ' ` )
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-26 20:06:51 -08:00
}
const response = await fetch ( ` ${ baseUrl } /arbitrary/resources/searchsimple? ${ urlSuffix } ` , {
method : 'GET' ,
headers : { 'accept' : 'application/json' }
2024-12-27 23:04:16 -08: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-26 20:06:51 -08:00
2024-12-27 23:04:16 -08:00
const data = await response . json ( )
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-26 20:06:51 -08:00
if ( ! Array . isArray ( data ) ) {
2024-12-27 23:04:16 -08:00
console . log ( "searchSimple: data is not an array?" , data )
return null
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-26 20:06:51 -08:00
}
if ( data . length === 0 ) {
2024-12-27 23:04:16 -08:00
console . log ( "searchSimple: no results found" )
return null // Return null when no items
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-26 20:06:51 -08:00
}
2024-12-27 23:04:16 -08:00
if ( limit === 1 ) {
console . log ( "searchSimple: limit=1 passed, only result returned" , data [ 0 ] )
return data [ 0 ] // Return just the single object
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-26 20:06:51 -08:00
}
2024-12-27 23:04:16 -08:00
console . log ( "searchSimple: multiple results returned" , data )
return data
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-26 20:06:51 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( "error during searchSimple" , error )
throw 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-26 20:06:51 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-14 19:40:31 -08:00
2024-12-11 14:40:32 -08: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-26 20:06:51 -08:00
2024-12-19 21:28:36 -08:00
const searchAllCountOnly = async ( query , room ) => {
2024-12-11 14:40:32 -08:00
try {
2024-12-27 23:04:16 -08:00
let offset = 0
const limit = 100 // Chunk size for fetching
let totalCount = 0
let hasMore = true
const qMintershipForumIdentifierPrefix = 'mintership-forum-message'
if ( ! query . includes ( qMintershipForumIdentifierPrefix ) ) {
try {
console . log ( ` 'mintership-forum-message' not found, switching to actual query... ` )
if ( room === "admins" ) {
while ( hasMore ) {
const response = await qortalRequest ( {
action : "SEARCH_QDN_RESOURCES" ,
service : "MAIL_PRIVATE" ,
query : query ,
limit : limit ,
offset : offset ,
mode : "ALL" ,
reverse : false
} )
if ( response && response . length > 0 ) {
totalCount += response . length
offset = totalCount
console . log ( ` Fetched ${ response . length } items, total count: ${ totalCount } , current offset: ${ offset } ` )
} else {
hasMore = false
}
}
} else {
// Fetch in chunks to accumulate the count
while ( hasMore ) {
const response = await qortalRequest ( {
action : "SEARCH_QDN_RESOURCES" ,
service : "BLOG_POST" ,
query : query ,
limit : limit ,
offset : offset ,
mode : "ALL" ,
reverse : false
} )
if ( response && response . length > 0 ) {
totalCount += response . length
offset = totalCount
console . log ( ` Fetched ${ response . length } items, total count: ${ totalCount } , current offset: ${ offset } ` )
} else {
hasMore = false
}
}
}
return totalCount
} catch ( error ) {
console . error ( "Error during SEARCH_QDN_RESOURCES:" , error )
throw error
}
}
2024-12-19 21:28:36 -08:00
2024-12-27 23:04:16 -08:00
if ( room === "admins" ) {
while ( hasMore ) {
const response = await searchSimple ( 'MAIL_PRIVATE' , query , '' , limit , offset , room , false )
if ( response && response . length > 0 ) {
totalCount += response . length
offset = totalCount
console . log ( ` Fetched ${ response . length } items, total count: ${ totalCount } , current offset: ${ offset } ` )
} else {
hasMore = false
}
2024-12-19 21:28:36 -08:00
}
2024-12-27 23:04:16 -08:00
} else {
while ( hasMore ) {
const response = await searchSimple ( 'BLOG_POST' , query , '' , limit , offset , room , false )
if ( response && response . length > 0 ) {
totalCount += response . length
offset = totalCount
console . log ( ` Fetched ${ response . length } items, total count: ${ totalCount } , current offset: ${ offset } ` )
} else {
hasMore = false
}
2024-12-19 21:28:36 -08:00
}
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
return totalCount
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( "Error during SEARCH_QDN_RESOURCES:" , error )
throw error
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
const searchResourcesWithStatus = async ( query , limit , status = 'local' ) => {
2024-12-27 23:04:16 -08:00
console . log ( 'searchResourcesWithStatus called' )
console . log ( 'query:' , query )
console . log ( 'limit:' , limit )
console . log ( 'status:' , status )
2024-12-11 14:40:32 -08:00
try {
// Set default limit if not provided or too low
if ( ! limit || limit < 10 ) {
2024-12-27 23:04:16 -08:00
limit = 200
2024-12-11 14:40:32 -08:00
}
// Make API request
const response = await fetch ( ` ${ baseUrl } /arbitrary/resources/search?query= ${ query } &includestatus=true&limit= ${ limit } &reverse=true ` , {
method : 'GET' ,
headers : { 'accept' : 'application/json' }
2024-12-27 23:04:16 -08:00
} )
2024-12-11 14:40:32 -08:00
2024-12-27 23:04:16 -08:00
const data = await response . json ( )
2024-12-11 14:40:32 -08:00
// Filter based on status if provided
if ( status ) {
if ( status === 'notLocal' ) {
2024-12-27 23:04:16 -08:00
const notDownloaded = data . filter ( ( resource ) => resource . status . status === 'published' )
console . log ( 'notDownloaded:' , notDownloaded )
return notDownloaded
2024-12-11 14:40:32 -08:00
} else if ( status === 'local' ) {
const downloaded = data . filter ( ( resource ) =>
resource . status . status === 'ready' ||
resource . status . status === 'downloaded' ||
resource . status . status === 'building' ||
( resource . status . status && resource . status . status !== 'published' )
2024-12-27 23:04:16 -08:00
)
return downloaded
2024-12-11 14:40:32 -08:00
}
}
// Return all data if no specific status is provided
2024-12-27 23:04:16 -08:00
console . log ( 'Returning all data...' )
return data
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error searching for resources with metadata:' , error )
throw error
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
const getResourceMetadata = async ( service , name , identifier ) => {
2024-12-27 23:04:16 -08:00
console . log ( 'getResourceMetadata called' )
console . log ( 'service:' , service )
console . log ( 'name:' , name )
console . log ( 'identifier:' , identifier )
2024-12-11 14:40:32 -08:00
try {
const response = await fetch ( ` ${ baseUrl } /arbitrary/metadata/ ${ service } / ${ name } / ${ identifier } ` , {
method : 'GET' ,
headers : { 'accept' : 'application/json' }
2024-12-27 23:04:16 -08:00
} )
const data = await response . json ( )
console . log ( 'Fetched resource metadata:' , data )
return data
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error fetching resource metadata:' , error )
throw error
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
const fetchFileBase64 = async ( service , name , identifier ) => {
2024-12-27 23:04:16 -08:00
const url = ` ${ baseUrl } /arbitrary/ ${ service } / ${ name } / ${ identifier } /?encoding=base64 `
2024-12-11 14:40:32 -08:00
try {
const response = await fetch ( url , {
method : 'GET' ,
headers : { 'accept' : 'text/plain' }
2024-12-27 23:04:16 -08:00
} )
return response
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( "Error fetching the image file:" , error )
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
async function loadImageHtml ( service , name , identifier , filename , mimeType ) {
try {
2024-12-27 23:04:16 -08:00
const url = ` ${ baseUrl } /arbitrary/ ${ service } / ${ name } / ${ identifier } `
2024-12-20 22:07:18 -08:00
// Fetch the file as a blob
2024-12-27 23:04:16 -08:00
const response = await fetch ( url )
2024-12-20 22:07:18 -08:00
// Convert the response to a Blob
2024-12-27 23:04:16 -08:00
const fileBlob = new Blob ( [ response ] , { type : mimeType } )
2024-12-20 22:07:18 -08:00
// Create an Object URL from the Blob
2024-12-27 23:04:16 -08:00
const objectUrl = URL . createObjectURL ( fileBlob )
2024-12-20 22:07:18 -08:00
// Use the Object URL as the image source
2024-12-27 23:04:16 -08:00
const attachmentHtml = ` <div class="attachment"><img src=" ${ objectUrl } " alt=" ${ filename } " class="inline-image"></div> `
2024-12-20 22:07:18 -08:00
2024-12-27 23:04:16 -08:00
return attachmentHtml
2024-12-20 22:07:18 -08:00
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( "Error fetching the image:" , error )
2024-12-11 14:40:32 -08:00
}
}
const fetchAndSaveAttachment = async ( service , name , identifier , filename , mimeType ) => {
2024-12-20 22:07:18 -08:00
try {
2024-12-24 00:27:17 -08:00
if ( ! filename || ! mimeType ) {
2024-12-27 23:04:16 -08:00
console . error ( "Filename and mimeType are required" )
return
2024-12-24 00:27:17 -08:00
}
// If it's a private file, we fetch with ?encoding=base64 and decrypt
if ( service === "MAIL_PRIVATE" ) {
2024-12-27 23:04:16 -08:00
service = "FILE_PRIVATE"
2024-12-24 00:27:17 -08:00
}
2024-12-27 23:04:16 -08:00
const baseUrlWithParams = ` ${ baseUrl } /arbitrary/ ${ service } / ${ name } / ${ identifier } ?async=true&attempts=5 `
2024-12-24 00:27:17 -08:00
if ( service === "FILE_PRIVATE" ) {
// 1) We want the encrypted base64
2024-12-27 23:04:16 -08:00
const urlPrivate = ` ${ baseUrlWithParams } &encoding=base64 `
2024-12-24 00:27:17 -08:00
const response = await fetch ( urlPrivate , {
method : 'GET' ,
headers : { 'accept' : 'text/plain' }
2024-12-27 23:04:16 -08:00
} )
2024-12-24 00:27:17 -08:00
if ( ! response . ok ) {
2024-12-27 23:04:16 -08:00
throw new Error ( ` File not found (HTTP ${ response . status } ): ${ urlPrivate } ` )
2024-12-20 22:07:18 -08:00
}
2024-12-24 00:27:17 -08:00
// 2) Get the encrypted base64 text
2024-12-27 23:04:16 -08:00
const encryptedBase64Data = await response . text ( )
console . log ( "Fetched Encrypted Base64 Data:" , encryptedBase64Data )
2024-12-24 00:27:17 -08:00
// 3) Decrypt => returns decrypted base64
2024-12-27 23:04:16 -08:00
const decryptedBase64 = await decryptObject ( encryptedBase64Data )
console . log ( "Decrypted Base64 Data:" , decryptedBase64 )
2024-12-24 00:27:17 -08:00
// 4) Convert that to a Blob
2024-12-27 23:04:16 -08:00
const fileBlob = base64ToBlob ( decryptedBase64 , mimeType )
2024-12-24 00:27:17 -08:00
// 5) Save the file using qortalRequest
await qortalRequest ( {
action : "SAVE_FILE" ,
blob : fileBlob ,
filename ,
mimeType
2024-12-27 23:04:16 -08:00
} )
console . log ( "Encrypted file saved successfully:" , filename )
2024-12-24 00:27:17 -08:00
} else {
// Normal, unencrypted file
const response = await fetch ( baseUrlWithParams , {
method : 'GET' ,
headers : { 'accept' : 'text/plain' }
2024-12-27 23:04:16 -08:00
} )
2024-12-24 00:27:17 -08:00
if ( ! response . ok ) {
2024-12-27 23:04:16 -08:00
throw new Error ( ` File not found (HTTP ${ response . status } ): ${ baseUrlWithParams } ` )
2024-12-19 21:59:28 -08:00
}
2024-12-24 00:27:17 -08:00
2024-12-27 23:04:16 -08:00
const blob = await response . blob ( )
2024-12-24 00:27:17 -08:00
await qortalRequest ( {
action : "SAVE_FILE" ,
blob ,
filename ,
mimeType
2024-12-27 23:04:16 -08:00
} )
console . log ( "File saved successfully:" , filename )
2024-12-24 00:27:17 -08:00
}
2024-12-20 22:07:18 -08:00
} catch ( error ) {
2024-12-24 00:27:17 -08:00
console . error (
` Error fetching or saving attachment (service: ${ service } , name: ${ name } , identifier: ${ identifier } ): ` ,
error
2024-12-27 23:04:16 -08:00
)
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-24 00:27:17 -08:00
2024-12-20 22:07:18 -08:00
2024-12-24 00:27:17 -08:00
/ * *
* Convert a base64 - encoded string into a Blob
* @ param { string } base64String - The base64 - encoded string ( unencrypted )
* @ param { string } mimeType - The MIME type of the file
* @ returns { Blob } The resulting Blob
* /
const base64ToBlob = ( base64String , mimeType ) => {
// Decode base64 to binary string
2024-12-27 23:04:16 -08:00
const binaryString = atob ( base64String )
2024-12-24 00:27:17 -08:00
// Convert binary string to Uint8Array
2024-12-27 23:04:16 -08:00
const len = binaryString . length
const bytes = new Uint8Array ( len )
2024-12-24 00:27:17 -08:00
for ( let i = 0 ; i < len ; i ++ ) {
2024-12-27 23:04:16 -08:00
bytes [ i ] = binaryString . charCodeAt ( i )
2024-12-24 00:27:17 -08:00
}
// Create a blob from the Uint8Array
2024-12-27 23:04:16 -08:00
return new Blob ( [ bytes ] , { type : mimeType } )
}
2024-12-24 00:27:17 -08:00
2024-12-20 22:07:18 -08:00
2024-12-24 00:27:17 -08:00
const fetchEncryptedImageBase64 = async ( service , name , identifier , mimeType ) => {
try {
// Fix potential typo: use &async=...
2024-12-27 23:04:16 -08:00
const urlPrivate = ` ${ baseUrl } /arbitrary/ ${ service } / ${ name } / ${ identifier } ?encoding=base64&async=true&attempts=5 `
2024-12-24 00:27:17 -08:00
const response = await fetch ( urlPrivate , {
method : 'GET' ,
headers : { 'accept' : 'text/plain' }
2024-12-27 23:04:16 -08:00
} )
2024-12-24 00:27:17 -08:00
if ( ! response . ok ) {
// Return null to "skip" the missing file
2024-12-27 23:04:16 -08:00
console . warn ( ` File not found (HTTP ${ response . status } ): ${ urlPrivate } ` )
return null
2024-12-24 00:27:17 -08:00
}
// 2) Read the base64 text
2024-12-27 23:04:16 -08:00
const encryptedBase64Data = await response . text ( )
console . log ( "Fetched Encrypted Base64 Data:" , encryptedBase64Data )
2024-12-24 00:27:17 -08:00
// 3) Decrypt => returns the *decrypted* base64 string
2024-12-27 23:04:16 -08:00
const decryptedBase64 = await decryptObject ( encryptedBase64Data )
console . log ( "Decrypted Base64 Data:" , decryptedBase64 )
2024-12-24 00:27:17 -08:00
// 4) Convert that decrypted base64 into a Blob
2024-12-27 23:04:16 -08:00
const fileBlob = base64ToBlob ( decryptedBase64 , mimeType )
2024-12-24 00:27:17 -08:00
// 5) (Optional) Create an object URL
2024-12-27 23:04:16 -08:00
const objectUrl = URL . createObjectURL ( fileBlob )
console . log ( "Object URL:" , objectUrl )
2024-12-24 00:27:17 -08:00
// Return the base64 or objectUrl, whichever you need
2024-12-27 23:04:16 -08:00
return decryptedBase64
2024-12-24 00:27:17 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( "Skipping file due to error in fetchEncryptedImageBase64:" , error )
return null // indicates "missing or failed"
2024-12-24 00:27:17 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-24 00:27:17 -08:00
2024-12-11 14:40:32 -08:00
2024-12-20 22:07:18 -08:00
2024-12-11 14:40:32 -08:00
const renderData = async ( service , name , identifier ) => {
2024-12-27 23:04:16 -08:00
console . log ( 'renderData called' )
console . log ( 'service:' , service )
console . log ( 'name:' , name )
console . log ( 'identifier:' , identifier )
2024-12-11 14:40:32 -08:00
try {
const response = await fetch ( ` ${ baseUrl } /render/ ${ service } / ${ name } ?identifier= ${ identifier } ` , {
method : 'GET' ,
headers : { 'accept' : '*/*' }
2024-12-27 23:04:16 -08:00
} )
2024-12-11 14:40:32 -08:00
// If the response is not OK (status 200-299), throw an error
if ( ! response . ok ) {
2024-12-27 23:04:16 -08:00
throw new Error ( 'Error rendering data' )
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
const responseText = await response . text ( )
2024-12-11 14:40:32 -08:00
// Check if the response includes <!DOCTYPE> indicating it's an HTML document
if ( responseText . includes ( '<!DOCTYPE' ) ) {
2024-12-27 23:04:16 -08:00
throw new Error ( 'Received HTML response' )
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
const data = JSON . parse ( responseText )
console . log ( 'Rendered data:' , data )
return data
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error rendering data:' , error )
2024-12-11 14:40:32 -08:00
// Return the custom message when there’ s an error or invalid data
2024-12-27 23:04:16 -08:00
return 'Requested data is either missing or still being obtained from QDN... please try again in a short time.'
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
const getProductDetails = async ( service , name , identifier ) => {
2024-12-27 23:04:16 -08:00
console . log ( 'getProductDetails called' )
console . log ( 'service:' , service )
console . log ( 'name:' , name )
console . log ( 'identifier:' , identifier )
2024-12-11 14:40:32 -08:00
try {
const response = await fetch ( ` ${ baseUrl } /arbitrary/metadata/ ${ service } / ${ name } / ${ identifier } ` , {
method : 'GET' ,
headers : { 'accept' : 'application/json' }
2024-12-27 23:04:16 -08:00
} )
const data = await response . json ( )
console . log ( 'Fetched product details:' , data )
return data
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( 'Error fetching product details:' , error )
throw error
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
// Qortal poll-related calls ----------------------------------------------------------------------
const fetchPollResults = async ( pollName ) => {
try {
const response = await fetch ( ` ${ baseUrl } /polls/votes/ ${ pollName } ` , {
method : 'GET' ,
headers : { 'Accept' : 'application/json' }
2024-12-27 23:04:16 -08:00
} )
const pollData = await response . json ( )
return pollData
2024-12-11 14:40:32 -08:00
} catch ( error ) {
2024-12-27 23:04:16 -08:00
console . error ( ` Error fetching poll results for ${ pollName } : ` , error )
return null
2024-12-11 14:40:32 -08:00
}
2024-12-27 23:04:16 -08:00
}
2024-12-11 14:40:32 -08:00
2024-12-16 19:53:37 -08:00
// Vote YES on a poll ------------------------------
const voteYesOnPoll = async ( poll ) => {
await qortalRequest ( {
action : "VOTE_ON_POLL" ,
pollName : poll ,
optionIndex : 0 ,
2024-12-27 23:04:16 -08:00
} )
2024-12-16 19:53:37 -08:00
}
// Vote NO on a poll -----------------------------
const voteNoOnPoll = async ( poll ) => {
await qortalRequest ( {
action : "VOTE_ON_POLL" ,
pollName : poll ,
optionIndex : 1 ,
2024-12-27 23:04:16 -08:00
} )
2024-12-16 19:53:37 -08:00
}
2024-12-11 14:40:32 -08:00
// export {
// userState,
// adminGroups,
// searchResourcesWithMetadata,
// searchResourcesWithStatus,
// getResourceMetadata,
// renderData,
// getProductDetails,
// getUserGroups,
// getUserAddress,
// login,
// timestampToHumanReadableDate,
// base64EncodeString,
// verifyUserIsAdmin,
// fetchAllDataByIdentifier,
// fetchOwnerAddressFromName,
// verifyAddressIsAdmin,
// uid,
// fetchAllGroups,
// getNameInfo,
// publishMultipleResources,
// getPublicKeyByName,
// objectToBase64,
// fetchMinterGroupAdmins
2024-12-27 23:04:16 -08:00
// }