import config from "@/utils/config.json"; import * as apiTypes from "@/utils/api/types"; /** * Adds a row to the user statistics card on the profile page allowing the user to quickly view & copy another user's ID. * @param userId The ID of the user. */ export async function displayId(userId: number) { const statsCard = document.getElementById('user-stats-card'); if (!statsCard) return; // ? Incase the user is blocked, which means the stats card won't be present const row = document.createElement('div'); row.classList.add('mb-1'); row.innerHTML = ` Player ID ${userId} `; const copyBtn = row.getElementsByTagName('a')[0]; copyBtn.addEventListener('click', () => { navigator.clipboard.writeText(userId as unknown as string) .then(() => { const icon: HTMLElement = copyBtn.children[0] as HTMLElement; copyBtn.classList.add('text-success'); icon.setAttribute('class', 'fa-duotone fa-circle-check'); icon.style.marginLeft = '3px'; setTimeout(() => { copyBtn.classList.remove('text-success'); icon.setAttribute('class', 'fad fa-copy'); icon.style.marginLeft = '5px'; }, 1500); }) .catch(() => { alert('Failure to copy user ID to clipboard.'); }); }); statsCard.children[0].insertBefore(row, statsCard.querySelector('.mb-1:has(.fa-calendar)')!); }; /** * Adds a button next to the "Avatar" card heading, which, on click, adds up all the items the user who owns this profile is wearing. * @param userId The ID of the user. */ export async function outfitCost(userId: number) { const calculateBtn = document.createElement('small'); calculateBtn.classList.add('fw-normal'); calculateBtn.style.letterSpacing = '0px'; const calculate = async function() { const outfit = { cost: 0, collectibles: 0, offsale: 0, timed: 0 }; const avatar: apiTypes.avatarApiSchema | "disabled" = await pullKVCache( 'avatars', userId.toString(), async () => { if (!config.api.enabled) return "disabled"; return (await (await fetch(config.api.urls.public + "users/" + userId + "/avatar")).json()); }, 30000, // 30 seconds false ); if (avatar == "disabled") { calculateBtn.innerHTML = 'Outfit cost unavailable, please try again later'; throw new Error('[Poly+] API is disabled, cancelling avatar cost loading..'); }; if (avatar.isDefault) { calculateBtn.innerHTML = 'Default avatar'; console.warn('[Poly+] User has default avatar, cancelling avatar cost loading..'); return; }; for (const asset of avatar.assets.filter((asset) => asset.type != "profileTheme")) { // ? API won't be disabled at this step, since there was already an API check previously const item: apiTypes.itemApiSchema = await pullKVCache( 'items', asset.id.toString(), async () => { return (await (await fetch(config.api.urls.public + "store/" + asset.id)).json()); }, 600000, false ); if (item.isLimited) { outfit.collectibles++; outfit.cost += item.averagePrice!; } else if (!item.price) { outfit.offsale++; } else if (item.onSaleUntil != null) { outfit.timed++; } else { outfit.cost += item.price!; }; }; console.log('[Poly+] Outfit breakdown: ', outfit); calculateBtn.innerHTML = `${ outfit.cost.toLocaleString() }`; }; if (config.api.enabled) { calculateBtn.innerHTML = '$ calculate avatar cost'; calculateBtn.children[0].addEventListener('click', calculate); } else { calculateBtn.innerHTML = 'Outfit cost unavailable, please try again later'; console.error("[Poly+] API is disabled, outfit cost calculation is unavailable.") } document.querySelector('.section-title:first-child')!.appendChild(calculateBtn); };