713 lines
24 KiB
JavaScript
713 lines
24 KiB
JavaScript
// Set the forumAdminGroups variable
|
||
let adminGroups = ["Q-Mintership-admin", "dev-group", "Mintership-Forum-Admins"];
|
||
|
||
// Settings to allow non-devmode development with 'live-server' module
|
||
let baseUrl = '';
|
||
let isOutsideOfUiDevelopment = false;
|
||
|
||
if (typeof qortalRequest === 'function') {
|
||
console.log('qortalRequest is available as a function. Setting development mode to false and baseUrl to nothing.');
|
||
isOutsideOfUiDevelopment = false;
|
||
baseUrl = '';
|
||
} else {
|
||
console.log('qortalRequest is not available as a function. Setting baseUrl to localhost.');
|
||
isOutsideOfUiDevelopment = true;
|
||
baseUrl = "http://localhost:12391";
|
||
};
|
||
|
||
// USEFUL UTILITIES ----- ----- -----
|
||
// Generate a short random ID to utilize at the end of unique identifiers.
|
||
const uid = async () => {
|
||
console.log('uid function called');
|
||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||
let result = '';
|
||
const charactersLength = characters.length;
|
||
for (let i = 0; i < 6; i++) {
|
||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||
};
|
||
console.log('Generated uid:', result);
|
||
return result;
|
||
};
|
||
// Turn a unix timestamp into a human-readable date
|
||
const timestampToHumanReadableDate = async(timestamp) => {
|
||
console.log('timestampToHumanReadableDate called');
|
||
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;
|
||
};
|
||
// Base64 encode a string
|
||
const base64EncodeString = async (str) => {
|
||
console.log('base64EncodeString called');
|
||
const encodedString = btoa(String.fromCharCode.apply(null, new Uint8Array(new TextEncoder().encode(str).buffer)));
|
||
console.log('Encoded string:', encodedString);
|
||
return encodedString;
|
||
};
|
||
|
||
const objectToBase64 = async (obj) => {
|
||
// Step 1: Convert the object to a JSON string
|
||
const jsonString = JSON.stringify(obj);
|
||
// Step 2: Create a Blob from the JSON string
|
||
const blob = new Blob([jsonString], { type: 'application/json' });
|
||
// Step 3: Create a FileReader to read the Blob as a base64-encoded string
|
||
return new Promise((resolve, reject) => {
|
||
const reader = new FileReader();
|
||
reader.onloadend = () => {
|
||
if (typeof reader.result === 'string') {
|
||
// Remove 'data:application/json;base64,' prefix
|
||
const base64 = reader.result.replace('data:application/json;base64,', '');
|
||
console.log(`base64 resolution: ${base64}`);
|
||
resolve(base64);
|
||
} else {
|
||
reject(new Error('Failed to read the Blob as a base64-encoded string'));
|
||
}
|
||
};
|
||
reader.onerror = () => {
|
||
reject(reader.error);
|
||
};
|
||
reader.readAsDataURL(blob);
|
||
});
|
||
};
|
||
|
||
// User state util
|
||
const userState = {
|
||
isLoggedIn: false,
|
||
accountName: null,
|
||
accountAddress: null,
|
||
isAdmin: false,
|
||
isMinterAdmin: false,
|
||
isForumAdmin: false
|
||
};
|
||
|
||
// USER-RELATED QORTAL CALLS ------------------------------------------
|
||
// Obtain the address of the authenticated user checking userState.accountAddress first.
|
||
const getUserAddress = async () => {
|
||
console.log('getUserAddress called');
|
||
try {
|
||
if (userState.accountAddress) {
|
||
console.log('User address found in state:', userState.accountAddress);
|
||
return userState.accountAddress;
|
||
};
|
||
const userAccount = await qortalRequest({ action: "GET_USER_ACCOUNT" });
|
||
if (userAccount) {
|
||
console.log('Account address:', userAccount.address);
|
||
userState.accountAddress = userAccount.address;
|
||
console.log('Account address added to state:', userState.accountAddress);
|
||
return userState.accountAddress;
|
||
};
|
||
} catch (error) {
|
||
console.error('Error fetching account address:', error);
|
||
throw error;
|
||
};
|
||
};
|
||
|
||
const fetchOwnerAddressFromName = async (name) => {
|
||
console.log('fetchOwnerAddressFromName called');
|
||
console.log('name:', name);
|
||
try {
|
||
const response = await fetch(`${baseUrl}/names/${name}`, {
|
||
headers: { 'Accept': 'application/json' },
|
||
method: 'GET',
|
||
});
|
||
const data = await response.json();
|
||
console.log('Fetched owner address:', data.owner);
|
||
return data.owner;
|
||
} catch (error) {
|
||
console.error('Error fetching owner address:', error);
|
||
return null;
|
||
};
|
||
};
|
||
|
||
const verifyUserIsAdmin = async () => {
|
||
console.log('verifyUserIsAdmin called (QortalApi.js)');
|
||
try {
|
||
const accountAddress = userState.accountAddress || await getUserAddress();
|
||
userState.accountAddress = accountAddress;
|
||
const userGroups = await getUserGroups(accountAddress);
|
||
const minterGroupAdmins = await fetchMinterGroupAdmins();
|
||
const isAdmin = await userGroups.some(group => adminGroups.includes(group.groupName))
|
||
const isMinterAdmin = minterGroupAdmins.members.some(admin => admin.member === userState.accountAddress && admin.isAdmin)
|
||
if (isMinterAdmin) {
|
||
userState.isMinterAdmin = true
|
||
}
|
||
if (isAdmin) {
|
||
userState.isAdmin = true;
|
||
userState.isForumAdmin = true
|
||
}
|
||
return userState.isAdmin;
|
||
} catch (error) {
|
||
console.error('Error verifying user admin status:', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
const verifyAddressIsAdmin = async (address) => {
|
||
console.log('verifyAddressIsAdmin called');
|
||
console.log('address:', address);
|
||
try {
|
||
if (!address) {
|
||
console.log('No address provided');
|
||
return false;
|
||
};
|
||
const userGroups = await getUserGroups(address);
|
||
const minterGroupAdmins = await fetchMinterGroupAdmins();
|
||
const isAdmin = await userGroups.some(group => adminGroups.includes(group.groupName))
|
||
const isMinterAdmin = minterGroupAdmins.members.some(admin => admin.member === address && admin.isAdmin)
|
||
if ((isMinterAdmin) || (isAdmin)) {
|
||
return true;
|
||
} else {
|
||
return false
|
||
};
|
||
} catch (error) {
|
||
console.error('Error verifying address admin status:', error);
|
||
throw error
|
||
}
|
||
};
|
||
|
||
const getNameInfo = async (name) => {
|
||
console.log('getNameInfo called');
|
||
console.log('name:', name);
|
||
try {
|
||
const response = await fetch(`${baseUrl}/names/${name}`);
|
||
const data = await response.json();
|
||
console.log('Fetched name info:', data);
|
||
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
|
||
};
|
||
} catch (error) {
|
||
console.log('Error fetching name info:', error);
|
||
return null;
|
||
}
|
||
};
|
||
|
||
const getPublicKeyByName = async (name) => {
|
||
console.log('getPublicKeyByName called');
|
||
console.log('name:', name);
|
||
try {
|
||
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;
|
||
} catch (error) {
|
||
console.log('Error obtaining public key from name:', error);
|
||
return null;
|
||
}
|
||
};
|
||
|
||
const getPublicKeyFromAddress = async (address) => {
|
||
console.log('getPublicKeyFromAddress called');
|
||
console.log('address:', address);
|
||
try {
|
||
const response = await fetch(`${baseUrl}/addresses/${address}`,{
|
||
method: 'GET',
|
||
headers: { 'Accept': 'application/json' }
|
||
});
|
||
const data = await response.json();
|
||
const publicKey = data.publicKey;
|
||
console.log('Fetched public key:', publicKey);
|
||
return publicKey;
|
||
} catch (error) {
|
||
console.log('Error fetching public key from address:', error);
|
||
return null;
|
||
}
|
||
};
|
||
|
||
const getAddressFromPublicKey = async (publicKey) => {
|
||
console.log('getAddressFromPublicKey called');
|
||
try {
|
||
const response = await fetch(`${baseUrl}/addresses/convert/${publicKey}`,{
|
||
method: 'GET',
|
||
headers: { 'Accept': 'text/plain' }
|
||
});
|
||
const data = await response.text();
|
||
const address = data;
|
||
console.log('Converted Address:', address);
|
||
return address;
|
||
} catch (error) {
|
||
console.log('Error converting public key to address:', error);
|
||
return null;
|
||
}
|
||
};
|
||
|
||
const login = async () => {
|
||
console.log('login called');
|
||
try {
|
||
if (userState.accountName && (userState.isAdmin || userState.isLoggedIn) && userState.accountAddress) {
|
||
console.log(`Account name found in userState: '${userState.accountName}', no need to call API...skipping API call.`);
|
||
return userState.accountName;
|
||
}
|
||
|
||
const accountAddress = userState.accountAddress || await getUserAddress();
|
||
const accountNames = await qortalRequest({
|
||
action: "GET_ACCOUNT_NAMES",
|
||
address: accountAddress,
|
||
});
|
||
|
||
if (accountNames) {
|
||
userState.isLoggedIn = true;
|
||
userState.accountName = accountNames[0].name;
|
||
userState.accountAddress = accountAddress;
|
||
console.log('All account names:', accountNames);
|
||
console.log('Main name (in state):', userState.accountName);
|
||
console.log('User has been logged in successfully!');
|
||
return userState.accountName;
|
||
} else {
|
||
throw new Error("No account names found. Are you logged in? Do you have a registered name?");
|
||
}
|
||
} catch (error) {
|
||
console.error('Error fetching account names:', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
const getNamesFromAddress = async (address) => {
|
||
try {
|
||
const response = await fetch(`${baseUrl}/names/address/${address}?limit=20`, {
|
||
method: 'GET',
|
||
headers: { 'Accept': 'application/json' }
|
||
});
|
||
const names = await response.json();
|
||
return names.length > 0 ? names[0].name : address; // Return name if found, else return address
|
||
} catch (error) {
|
||
console.error(`Error fetching names for address ${address}:`, error);
|
||
return address;
|
||
}
|
||
};
|
||
|
||
|
||
// QORTAL GROUP-RELATED CALLS ------------------------------------------
|
||
const getUserGroups = async (userAddress) => {
|
||
console.log('getUserGroups called');
|
||
console.log('userAddress:', userAddress);
|
||
try {
|
||
if (!userAddress && userState.accountAddress) {
|
||
console.log('No address passed to getUserGroups call... using address from state...');
|
||
userAddress = userState.accountAddress;
|
||
}
|
||
const response = await fetch(`${baseUrl}/groups/member/${userAddress}`, {
|
||
method: 'GET',
|
||
headers: { 'accept': 'application/json' }
|
||
});
|
||
const data = await response.json();
|
||
console.log('Fetched user groups:', data);
|
||
return data;
|
||
} catch (error) {
|
||
console.error('Error fetching user groups:', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
const fetchMinterGroupAdmins = async () => {
|
||
console.log('calling for minter admins')
|
||
const response = await fetch(`${baseUrl}/groups/members/694?onlyAdmins=true&limit=0&reverse=true`,{
|
||
method: 'GET',
|
||
headers: { 'Accept': 'application/json' }
|
||
});
|
||
const admins = await response.json();
|
||
console.log('Fetched minter admins', admins);
|
||
return admins;
|
||
}
|
||
|
||
const fetchMinterGroupMembers = async () => {
|
||
try {
|
||
const response = await fetch(`${baseUrl}/groups/members/694?limit=0`, {
|
||
method: 'GET',
|
||
headers: { 'Accept': 'application/json' },
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||
}
|
||
|
||
const data = await response.json();
|
||
|
||
// Ensure the structure of the response is as expected
|
||
if (!Array.isArray(data.members)) {
|
||
throw new Error("Expected 'members' to be an array but got a different structure");
|
||
}
|
||
|
||
return data.members; // Assuming 'members' is the key in the response JSON
|
||
} catch (error) {
|
||
console.error("Error fetching minter group members:", error);
|
||
return []; // Return an empty array to prevent further errors
|
||
}
|
||
};
|
||
|
||
|
||
|
||
|
||
const fetchAllGroups = async (limit) => {
|
||
console.log('fetchAllGroups called');
|
||
console.log('limit:', limit);
|
||
if (!limit) {
|
||
limit = 2000;
|
||
}
|
||
try {
|
||
const response = await fetch(`${baseUrl}/groups?limit=${limit}&reverse=true`);
|
||
const data = await response.json();
|
||
console.log('Fetched all groups:', data);
|
||
return data;
|
||
} catch (error) {
|
||
console.error('Error fetching all groups:', error);
|
||
}
|
||
};
|
||
|
||
// QDN data calls
|
||
const searchLatestDataByIdentifier = async (identifier) => {
|
||
console.log('fetchAllDataByIdentifier called');
|
||
console.log('identifier:', 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' }
|
||
});
|
||
const latestData = await response.json();
|
||
console.log('Fetched latest data by identifier:', latestData);
|
||
return latestData;
|
||
} catch (error) {
|
||
console.error('Error fetching latest published data:', error);
|
||
return null;
|
||
}
|
||
};
|
||
|
||
const publishMultipleResources = async (resources, publicKeys = null, isPrivate = false) => {
|
||
console.log('publishMultipleResources called');
|
||
console.log('resources:', resources);
|
||
|
||
const request = {
|
||
action: 'PUBLISH_MULTIPLE_QDN_RESOURCES',
|
||
resources: resources,
|
||
};
|
||
|
||
if (isPrivate && publicKeys) {
|
||
request.encrypt = true;
|
||
request.publicKeys = publicKeys;
|
||
};
|
||
|
||
try {
|
||
const response = await qortalRequest(request);
|
||
console.log('Multiple resources published successfully:', response);
|
||
} catch (error) {
|
||
console.error('Error publishing multiple resources:', error);
|
||
};
|
||
};
|
||
|
||
|
||
const searchResourcesWithMetadata = async (query, limit) => {
|
||
console.log('searchResourcesWithMetadata called');
|
||
try {
|
||
if (limit == 0) {
|
||
limit = 0;
|
||
} else if (!limit || (limit < 10 && limit != 0)) {
|
||
limit = 200;
|
||
}
|
||
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' }
|
||
});
|
||
const data = await response.json();
|
||
console.log('Search results with metadata:', data);
|
||
return data;
|
||
} catch (error) {
|
||
console.error('Error searching for resources with metadata:', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
const searchAllResources = async (query, limit, after, reverse=false) => {
|
||
console.log('searchAllResources called. Query:', query, 'Limit:', limit,'Reverse:', reverse);
|
||
try {
|
||
if (limit == 0) {
|
||
limit = 0;
|
||
}
|
||
if (!limit || (limit < 10 && limit != 0)) {
|
||
limit = 200;
|
||
}
|
||
if (after == null || after === undefined) {
|
||
after = 0;
|
||
}
|
||
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' }
|
||
});
|
||
const data = await response.json();
|
||
console.log('Search results with metadata:', data);
|
||
return data;
|
||
} catch (error) {
|
||
console.error('Error searching for resources with metadata:', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
const searchAllWithOffset = async (query, limit, offset) =>{
|
||
try {
|
||
const response = await qortalRequest({
|
||
action: "SEARCH_QDN_RESOURCES",
|
||
service: "BLOG_POST",
|
||
query: query,
|
||
limit: limit,
|
||
offset: offset,
|
||
mode: "ALL",
|
||
reverse: false
|
||
});
|
||
return response
|
||
} catch (error) {
|
||
console.error("Error during SEARCH_QDN_RESOURCES:", error);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
const searchAllCountOnly = async (query) => {
|
||
try {
|
||
let offset = 0;
|
||
const limit = 100; // Chunk size for fetching
|
||
let totalCount = 0;
|
||
let hasMore = true;
|
||
|
||
// 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 += limit;
|
||
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;
|
||
}
|
||
}
|
||
|
||
const searchResourcesWithStatus = async (query, limit, status = 'local') => {
|
||
console.log('searchResourcesWithStatus called');
|
||
console.log('query:', query);
|
||
console.log('limit:', limit);
|
||
console.log('status:', status);
|
||
try {
|
||
// Set default limit if not provided or too low
|
||
if (!limit || limit < 10) {
|
||
limit = 200;
|
||
}
|
||
// 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' }
|
||
});
|
||
|
||
const data = await response.json();
|
||
// Filter based on status if provided
|
||
if (status) {
|
||
if (status === 'notLocal') {
|
||
const notDownloaded = data.filter((resource) => resource.status.status === 'published');
|
||
console.log('notDownloaded:', notDownloaded);
|
||
return notDownloaded;
|
||
} 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')
|
||
);
|
||
return downloaded;
|
||
}
|
||
}
|
||
// Return all data if no specific status is provided
|
||
console.log('Returning all data...');
|
||
return data;
|
||
} catch (error) {
|
||
console.error('Error searching for resources with metadata:', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
const getResourceMetadata = async (service, name, identifier) => {
|
||
console.log('getResourceMetadata called');
|
||
console.log('service:', service);
|
||
console.log('name:', name);
|
||
console.log('identifier:', identifier);
|
||
try {
|
||
const response = await fetch(`${baseUrl}/arbitrary/metadata/${service}/${name}/${identifier}`, {
|
||
method: 'GET',
|
||
headers: { 'accept': 'application/json' }
|
||
});
|
||
const data = await response.json();
|
||
console.log('Fetched resource metadata:', data);
|
||
return data;
|
||
} catch (error) {
|
||
console.error('Error fetching resource metadata:', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
const fetchFileBase64 = async (service, name, identifier) => {
|
||
const url = `${baseUrl}/arbitrary/${service}/${name}/${identifier}/?encoding=base64`;
|
||
try {
|
||
const response = await fetch(url,{
|
||
method: 'GET',
|
||
headers: { 'accept': 'text/plain' }
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error("Error fetching the image file:", error);
|
||
}
|
||
};
|
||
|
||
async function loadImageHtml(service, name, identifier, filename, mimeType) {
|
||
try {
|
||
const url = `${baseUrl}/arbitrary/${service}/${name}/${identifier}`;
|
||
// Fetch the file as a blob
|
||
const response = await fetch(url);
|
||
// Convert the response to a Blob
|
||
const fileBlob = new Blob([response], { type: mimeType });
|
||
// Create an Object URL from the Blob
|
||
const objectUrl = URL.createObjectURL(fileBlob);
|
||
// Use the Object URL as the image source
|
||
const attachmentHtml = `<div class="attachment"><img src="${objectUrl}" alt="${filename}" class="inline-image"></div>`;
|
||
|
||
return attachmentHtml;
|
||
|
||
} catch (error) {
|
||
console.error("Error fetching the image:", error);
|
||
}
|
||
}
|
||
|
||
const fetchAndSaveAttachment = async (service, name, identifier, filename, mimeType) => {
|
||
const url = `${baseUrl}/arbitrary/${service}/${name}/${identifier}`;
|
||
try {
|
||
const response = await fetch(url);
|
||
const blob = await response.blob();
|
||
await qortalRequest({
|
||
action: "SAVE_FILE",
|
||
blob,
|
||
filename: filename,
|
||
mimeType
|
||
});
|
||
} catch (error) {
|
||
console.error("Error fetching or saving the attachment:", error);
|
||
}
|
||
}
|
||
|
||
const renderData = async (service, name, identifier) => {
|
||
console.log('renderData called');
|
||
console.log('service:', service);
|
||
console.log('name:', name);
|
||
console.log('identifier:', identifier);
|
||
|
||
try {
|
||
const response = await fetch(`${baseUrl}/render/${service}/${name}?identifier=${identifier}`, {
|
||
method: 'GET',
|
||
headers: { 'accept': '*/*' }
|
||
});
|
||
// If the response is not OK (status 200-299), throw an error
|
||
if (!response.ok) {
|
||
throw new Error('Error rendering data');
|
||
}
|
||
|
||
const responseText = await response.text();
|
||
// Check if the response includes <!DOCTYPE> indicating it's an HTML document
|
||
if (responseText.includes('<!DOCTYPE')) {
|
||
throw new Error('Received HTML response');
|
||
}
|
||
|
||
const data = JSON.parse(responseText);
|
||
console.log('Rendered data:', data);
|
||
return data;
|
||
|
||
} catch (error) {
|
||
console.error('Error rendering data:', error);
|
||
// Return the custom message when there’s an error or invalid data
|
||
return 'Requested data is either missing or still being obtained from QDN... please try again in a short time.';
|
||
}
|
||
};
|
||
|
||
const getProductDetails = async (service, name, identifier) => {
|
||
console.log('getProductDetails called');
|
||
console.log('service:', service);
|
||
console.log('name:', name);
|
||
console.log('identifier:', identifier);
|
||
try {
|
||
const response = await fetch(`${baseUrl}/arbitrary/metadata/${service}/${name}/${identifier}`, {
|
||
method: 'GET',
|
||
headers: { 'accept': 'application/json' }
|
||
});
|
||
const data = await response.json();
|
||
console.log('Fetched product details:', data);
|
||
return data;
|
||
} catch (error) {
|
||
console.error('Error fetching product details:', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
|
||
// Qortal poll-related calls ----------------------------------------------------------------------
|
||
|
||
const fetchPollResults = async (pollName) => {
|
||
try {
|
||
const response = await fetch(`${baseUrl}/polls/votes/${pollName}`, {
|
||
method: 'GET',
|
||
headers: { 'Accept': 'application/json' }
|
||
});
|
||
const pollData = await response.json();
|
||
return pollData;
|
||
} catch (error) {
|
||
console.error(`Error fetching poll results for ${pollName}:`, error);
|
||
return null;
|
||
}
|
||
};
|
||
|
||
// export {
|
||
// userState,
|
||
// adminGroups,
|
||
// searchResourcesWithMetadata,
|
||
// searchResourcesWithStatus,
|
||
// getResourceMetadata,
|
||
// renderData,
|
||
// getProductDetails,
|
||
// getUserGroups,
|
||
// getUserAddress,
|
||
// login,
|
||
// timestampToHumanReadableDate,
|
||
// base64EncodeString,
|
||
// verifyUserIsAdmin,
|
||
// fetchAllDataByIdentifier,
|
||
// fetchOwnerAddressFromName,
|
||
// verifyAddressIsAdmin,
|
||
// uid,
|
||
// fetchAllGroups,
|
||
// getNameInfo,
|
||
// publishMultipleResources,
|
||
// getPublicKeyByName,
|
||
// objectToBase64,
|
||
// fetchMinterGroupAdmins
|
||
// };
|