$(document).ready(function () { const defaultSettings = { settings: { exchanges: 'binance', marketplaces: 'opensea', priceAs: 'fiat', currency: 'USD', displayETHTotal: false, isMasked: false, darkMode: true, countPudgyGroup: true, // is Pudgy values count in total? showPudgyGroup: true, // show or hide Pudgy area expPudgyGroup: true, // expand or collapse Pudgy area countAbsGroup: true, // is Abs values count in total? showAbsGroup: true, // show or hide Abs area expAbsGroup: true, // expand or collapse Abs area autoRefresh: false, // Refresh wallet when holding $Huddle }, address: { eth: [], sol: [], abs: [], }, token: { pengu: { total: 0 } }, collections: { pudgyrods: { contract_address: '0x062e691c2054de82f28008a8ccc6d7a1c8ce060d', slug: 'pudgyrods', qty: 0 }, lilpudgys: { contract_address: '0x524cab2ec69124574082676e6f654a18df49a048', slug: 'lilpudgys', qty: 0 }, pudgypenguins: { contract_address: '0xbd3531da5cf5857e7cfaa92426877b022e612cf8', slug: 'pudgypenguins', qty: 0 } }, abs: { } }; let userData = JSON.parse(localStorage.getItem('userData')); if (!userData) { userData = defaultSettings; localStorage.setItem('userData', JSON.stringify(userData)); let $popoverElement = $('#firstWelcomeLink'); $popoverElement.popover(); $popoverElement.popover('show'); setTimeout(function () { $popoverElement.popover('hide'); }, 5500); }else{ if (!userData.hasOwnProperty('abs')) { userData.abs = {}; localStorage.setItem('userData', JSON.stringify(userData)); } } function updateData(populate = true) { const $dataElements = $('[data-sync-key]'); userData = JSON.parse(localStorage.getItem('userData')); if (populate == true) { populateABSData(userData.abs); } $dataElements.addClass('skeleton-bg'); if (userData.settings.isMasked == false){ $('#pengu_amount').text(userData.token.pengu.total.toLocaleString('en-US')); $('#qty_pudgypenguins').text(userData.collections.pudgypenguins.qty); $('#qty_lilpudgys').text(userData.collections.lilpudgys.qty); $('#qty_pudgyrods').text(userData.collections.pudgyrods.qty); } $('#pengu_amount').data('original', userData.token.pengu.total.toLocaleString('en-US')); $('#qty_pudgypenguins').data('original', userData.collections.pudgypenguins.qty); $('#qty_lilpudgys').data('original', userData.collections.lilpudgys.qty); $('#qty_pudgyrods').data('original', userData.collections.pudgyrods.qty); var wait = 0; if (populate == true) { wait = 1000; } setTimeout(function (){ $.ajax({ url: '/ajax/hb', method: 'POST', data: { huddle_xFkiLamdyJgamk:'fb683d0ee1eb9e48ca04e1bda17edbea', data: userData, }, success: function (data) { var response = jQuery.parseJSON(data); if (userData.settings.isMasked == false){ $('#total_eth_amount').text(response?.data?.Z9W2M5X3); $('#total_fiat').text($('#total_fiat').data('prefix') + response?.data?.Y4C6L7J9); }else{ $('#total_fiat').data('original', response?.data?.Y4C6L7J9); $('#total_eth_amount').data('original', response?.data?.Z9W2M5X3); } $('#floor_pudgypenguins').text(response?.data?.J9B4M7K2); $('#floor_lilpudgys').text(response?.data?.O3F7M9X4); $('#floor_pudgyrods').text(response?.data?.T8B4J9P3); if (userData.settings.priceAs == 'fiat') { if (userData.settings.isMasked == false){ $('#total_pudgypenguins').text(response?.data?.M8D9C4N7); $('#total_lilpudgys').text(response?.data?.R1M3Y7L6); $('#total_pudgyrods').text(response?.data?.W3Y9X5M1); $('#total_pengu_fiat').text(response?.data?.E4L7C2Z6); } $('#total_pudgypenguins').data('original', response?.data?.M8D9C4N7); $('#total_lilpudgys').data('original', response?.data?.R1M3Y7L6); $('#total_pudgyrods').data('original', response?.data?.W3Y9X5M1); $('#total_pengu_fiat').data('original', response?.data?.E4L7C2Z6); }else{ if (userData.settings.isMasked == false){ $('#total_pudgypenguins').text(response?.data?.N2P6L1J3); $('#total_lilpudgys').text(response?.data?.S6X5C2N9); $('#total_pudgyrods').text(response?.data?.X2P4B8T6); $('#total_pengu_fiat').text(response?.data?.A5N3Y6V4); } $('#total_pudgypenguins').data('original', response?.data?.N2P6L1J3); $('#total_lilpudgys').data('original', response?.data?.S6X5C2N9); $('#total_pudgyrods').data('original', response?.data?.X2P4B8T6); $('#total_pengu_fiat').data('original', response?.data?.A5N3Y6V4); } $('#pengu_price_fiat').text(response?.data?.B9D6W3P8); $('#huddle_price').text(response?.data?.G3W5P8D2); $('#eth_price').text(response?.data?.H7C2X9M4); $('#sol_price').text(response?.data?.BJK79HBM); const abstokens = response?.data?.abstokens || {}; const absnfts = response?.data?.absnfts || {}; $.each(abstokens, function(key, info) { var k = String(key).toLowerCase(); // adjust if IDs aren't lowercase var total = Number(info?.total) || 0; var totalETH = Number(info?.totalETH) || 0; var price = Number(info?.price) || 0; // Update total_{key}_fiat var $totalEl = $(`#total_${k}_fiat`); if ($totalEl.length) { if (userData.settings.priceAs == 'fiat') { var formattedTotal = total.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) $totalEl.attr('data-original', formattedTotal); if (userData.settings.isMasked == false){ $totalEl.text(formattedTotal); } }else{ var formattedTotalETH = totalETH.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) $totalEl.attr('data-original', formattedTotalETH); if (userData.settings.isMasked == false){ $totalEl.text(formattedTotalETH); } } } // Update {key}_price_fiat var $priceEl = $(`#${k}_price_fiat`); if ($priceEl.length) { $priceEl.text(price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 7 })); } }); $.each(absnfts, function(key, info) { var k = String(key).toLowerCase(); // adjust if IDs aren't lowercase var total = Number(info?.total) || 0; var totalETH = Number(info?.totalETH) || 0; var price = Number(info?.price) || 0; // Update total_{key}_fiat var $totalEl = $(`#total_${k}`); if ($totalEl.length) { if (userData.settings.priceAs == 'fiat') { if (userData.settings.isMasked == false){ $totalEl.text(total.toLocaleString()); } $totalEl.attr('data-original', total.toLocaleString()); }else{ if (userData.settings.isMasked == false){ $totalEl.text(totalETH.toLocaleString()); } $totalEl.attr('data-original', totalETH.toLocaleString()); } } // Update {key}_price_fiat var $priceEl = $(`#floor_${k}`); if ($priceEl.length) { $priceEl.text(price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 7 })); } }); setTimeout(() => { $dataElements.removeClass('skeleton-bg'); }, 500); }, error: function () { $dataElements.removeClass('skeleton-bg'); } }); }, wait); } let pollingInterval; const pollingTime = 4500; const pollingUrl = '/ajax/update'; function resetCountdownBar() { const $bar = $('#countdown-bar'); $bar.removeClass('animate'); void $bar[0].offsetWidth; $bar.addClass('animate'); } function startPolling(runPop = true) { resetCountdownBar(); updateData(runPop); if (pollingInterval) return; pollingInterval = setInterval(() => { resetCountdownBar(); updateData(false); }, pollingTime); } function stopPolling() { clearInterval(pollingInterval); pollingInterval = null; $('#countdown-bar').removeClass('animate'); } function updateCurrencyLabel() { const priceAs = userData.settings.priceAs; const currency = userData.settings.currency; $('.main_currency_label').text(currency); if (priceAs === 'fiat') { $('.currency_label').text(currency); $('.pengu_total_currency_label').text(currency); } else { $('.currency_label').text('ETH'); $('.pengu_total_currency_label').text('SOL'); } } function updateSetting(key, value) { userData.settings[key] = value; localStorage.setItem('userData', JSON.stringify(userData)); if (key !== 'isMasked') { updateData(false); } } function getSelectedSettings() { return userData.settings; } function debounce(func, wait) { let timeout; return function (...args) { const later = () => { clearTimeout(timeout); func.apply(this, args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } function updatePillStyle($pill, selected) { const brandColor = $pill.data('brand-color'); const brandDarkColor = $pill.data('brand-dark-color'); const $img = $pill.find('img'); const isDarkMode = $('body').hasClass('dark-mode'); if ($img.length) { let src = $img.attr('src'); let srcset = $img.attr('srcset'); if (selected) { src = src.replace(/\/([^\/]+?)(?:_on)?(@\dx\.png)/, '/$1_on$2'); srcset = srcset.replace(/\/([^\/]+?)(?:_on)?(@\dx\.png)/g, '/$1_on$2'); } else { src = src.replace(/_on(?=@\dx\.png)/, ''); srcset = srcset.replace(/_on(?=@\dx\.png)/g, ''); } $img.attr('src', src); $img.attr('srcset', srcset); } const backgroundColor = selected ? (isDarkMode ? brandDarkColor : brandColor) : (isDarkMode ? '#aaa' : '#fff'); const borderStyle = selected ? (isDarkMode ? `1px solid ${brandDarkColor}` : `1px solid ${brandColor}`) : '0px'; $pill.css({ backgroundColor: backgroundColor, border: borderStyle }); } function restoreSelections() { const settings = userData.settings; applyDarkMode(settings.darkMode); $('.group-content').each(function () { const group = $(this).data('group'); const key = `exp${capitalize(group)}Group`; const $icon = $(`.hm-group-title.${group} .collapse-toggle i`); if (userData.settings[key] === false) { $(this).hide(); $icon.removeClass('uil-angle-down').addClass('uil-angle-up'); } }); ['exchanges', 'marketplaces', 'priceAs'].forEach(key => { const attr = key === 'priceAs' ? 'price-as' : key; $(`[data-${attr}]`).each(function () { const $pill = $(this); if ($pill.data(attr) === settings[key]) { $pill.addClass('selected'); updatePillStyle($pill, true); } }); }); $('#fm_currency').val(settings.currency); $('input[name="displayETHTotal"]').prop('checked', settings.displayETHTotal); $('input[name="autoRefresh"]').prop('checked', settings.autoRefresh); $('#total_eth').toggle(settings.displayETHTotal); applyMasking(settings.isMasked); updateCurrencyLabel(); } function applyDarkMode(darkMode) { if (darkMode) { document.body.classList.add('dark-mode'); $('#toggleDarkMode').prop('checked', true); } else { document.body.classList.remove('dark-mode'); $('#toggleDarkMode').prop('checked', false); } } function applyMasking(isMasked) { if (isMasked) { $('#showhideeye').removeClass('uil-eye').addClass('uil-eye-slash'); $('[data-maskable]').each(function () { const $this = $(this); const prefix = $this.data('prefix') || ''; const len = $this.data('len') || 6; const masked = '*'.repeat(len); $this.text(prefix + masked); }); } else { $('#showhideeye').removeClass('uil-eye-slash').addClass('uil-eye'); $('[data-maskable]').each(function () { const $this = $(this); const prefix = $this.data('prefix') || ''; const original = $this.data('original'); $this.text(prefix + original); }); } } function masking() { const isEyeVisible = $('#showhideeye').hasClass('uil-eye'); const newState = isEyeVisible; $('#showhideeye').toggleClass('uil-eye uil-eye-slash'); $('[data-maskable]').each(function () { const $this = $(this); const prefix = $this.data('prefix') || ''; const original = $this.data('original'); const len = $this.data('len') || 6; if (isEyeVisible) { const masked = '*'.repeat(len); $this.text(prefix + masked); } else { $this.text(prefix + original); } }); updateSetting('isMasked', newState); } $('#showhideeye').on('click', masking); $('.option-pill').on('click', function () { const $pill = $(this); const $row = $pill.closest('.option-row'); $row.find('.option-pill').each(function () { $(this).removeClass('selected'); updatePillStyle($(this), false); }); $pill.addClass('selected'); updatePillStyle($pill, true); ['exchanges', 'marketplaces', 'price-as'].forEach(attr => { const value = $pill.data(attr); if (value !== undefined) { const key = attr === 'price-as' ? 'priceAs' : attr; updateSetting(key, value); } }); updateCurrencyLabel(); }); $('#fm_currency').on('change', function () { updateSetting('currency', $(this).val()); updateCurrencyLabel(); }); $('input[name="displayETHTotal"]').on('change', function () { const isChecked = $(this).is(':checked'); updateSetting('displayETHTotal', isChecked); $('#total_eth').toggle(isChecked); }); $('input[name="autoRefresh"]').on('change', function () { var $checkbox = $(this); var isChecked = $checkbox.is(':checked'); if (isChecked && hasHuddle() === false) { var msg = 'Please trade some $Huddle to enable this feature'; $('#refreshMsg').html(msg).fadeIn(); $checkbox.prop('checked', false); setTimeout(function () { $('#refreshMsg').fadeOut(); }, 4000); return; } updateSetting('autoRefresh', isChecked); }); $('#offcanvas-settings').on('shown.bs.offcanvas', function () { stopPolling(); }); $('#offcanvas-settings').on('hidden.bs.offcanvas', function () { startPolling(false); }); $('#modal-wallet').on('shown.bs.modal', function () { stopPolling(); }); $('#modal-wallet').on('hidden.bs.modal', function () { startPolling(false); }); restoreSelections(); var baseTime = 900; var additionalTime = 0; var waitTime = baseTime; var targetTokenAddress = "0x143bd0eac0a811aac43e8aae5d28b624b6b63489"; if (userData && userData.abs) { var absWallets = userData.abs; var walletAddresses = Object.keys(absWallets); var userSettings = userData.settings; var hasTargetToken = walletAddresses.some(address => { var walletData = absWallets[address]; var tokens = walletData.token || []; return tokens.some(token => token.c === targetTokenAddress); }); if (hasTargetToken && userSettings.autoRefresh == true) { var delayPerWallet = 400; walletAddresses.forEach(function(address, index) { setTimeout(function () { var entry = $('.wallet-entry[data-wallet-address="' + address + '"]'); var syncIcon = entry.find('.uil-sync'); if (syncIcon.length) { syncIcon.trigger('click'); } }, index * delayPerWallet); }); additionalTime = delayPerWallet * walletAddresses.length; waitTime = baseTime + additionalTime; setTimeout(function(){ startPolling(); }, 1200); }else{ startPolling(); } } applyDarkMode(userData.settings.darkMode); $('#captureBtn').on('click', function () { const captureArea = document.querySelector('#captureArea'); html2canvas(captureArea).then(canvas => { $('#hm-teaser').css('max-height','50px'); $('#hm-teaser button').hide(); canvas.toBlob(blob => { const imgFile = new File([blob], 'my-huddle-value.png', { type: 'image/png' }); if (/iPhone|iPad|iPod/i.test(navigator.userAgent)) { if (navigator.canShare && navigator.canShare({ files: [imgFile] })) { navigator.share({ files: [imgFile], title: 'My Huddle Value' }).then(() => { $('#statusMessage').text('Image shared successfully!'); }).catch(err => { console.error('Sharing failed:', err); $('#statusMessage').text('Failed to share the image.'); }); } else { $('#statusMessage').text('Sharing not supported on this device.'); } } else { if (navigator.clipboard && navigator.clipboard.write) { const clipboardItem = new ClipboardItem({ 'image/png': blob }); navigator.clipboard.write([clipboardItem]).then(() => { $('#statusMessage').text('Image copied to clipboard!'); }).catch(err => { console.error('Failed to copy:', err); $('#statusMessage').text('Failed to copy image.'); }); } else { console.error('Clipboard API not supported.'); $('#statusMessage').text('Clipboard API not supported.'); } } }, 'image/png'); }).catch(err => { console.error('Error capturing the area:', err); $('#statusMessage').text('Error capturing the area.'); }); }); const $input = $('#fm_wallet_address'); const $button = $('#address_form button'); const $result = $('#wallet_result'); $input.on('input', debounce(function () { const address = $input.val().trim(); $button.prop('disabled', !(address.length >= 32 && address.length <= 44)); if (address.length < 32) return; let walletType = null; if (/^0x[a-fA-F0-9]{40}$/.test(address)) { walletType = 'eth'; } else if (/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address)) { walletType = 'sol'; } else { return; } $input.addClass('input-loading'); if (userData.address[walletType]?.some(w => w.wallet === address)) { $input.removeClass('input-loading'); return; } $.ajax({ url: '/ajax/wallet', method: 'POST', data: { huddle_xFkiLamdyJgamk:'fb683d0ee1eb9e48ca04e1bda17edbea', address: address, walletType: walletType }, success: function (data) { $input.removeClass('input-loading'); var response = jQuery.parseJSON(data); if (!userData.address[response.return.type].some(w => w.wallet === response.return.wallet)) { const walletType = response.return.type; const wallet = response.return.wallet; const balances = response.return.balances; const balanceObj = {}; let iconsHTML = ""; let absTokens = []; let absNFTs = []; let abstokenCount = 0; let absnftCount = 0; balances.forEach(bal => { const quantity = parseFloat(bal.quantity || 0); if (quantity === 0) return; if (bal.type === 'token') { userData.token.pengu.total += quantity; balanceObj[bal.name] = quantity; const penguFormatted = formatPengu(quantity); iconsHTML += createWalletIcon( "/assets/img/0xbd3531da5cf5857e7cfaa92426877b022e612cf8.avif", penguFormatted + ' $PENGU', "50%" ); } else if (bal.type === 'nft') { if (userData.collections[bal.name]) { userData.collections[bal.name].qty += quantity; balanceObj[bal.name] = quantity; const logoSlug = userData.collections[bal.name].contract_address; iconsHTML += createWalletIcon( `/assets/img/${logoSlug}.avif`, quantity, "5px" ); } } else if (bal.type === 'abstoken') { absTokens.push({ c: bal.address, n: bal.name, b: quantity }); abstokenCount++; } else if (bal.type === 'absnft') { absNFTs.push({ c: bal.address, n: bal.name, b: quantity }); absnftCount++; } }); // Store Abstract tokens & NFTs in abs if (walletType === 'abs' || absTokens.length > 0 || absNFTs.length > 0) { userData.abs[wallet] = { token: absTokens, nft: absNFTs }; // Append icons for Abstract (if any holdings) if (absTokens.length > 0) { iconsHTML += createWalletIcon( "/assets/img/vendor/abstract_logo_sq.png", `${absTokens.length} tokens`, "50%" ); } if (absNFTs.length > 0) { iconsHTML += createWalletIcon( "/assets/img/vendor/abstract_logo_sq.png", `${absNFTs.length} collections`, "5px" ); } } userData.address[walletType].push({ wallet: wallet, balance: balanceObj }); localStorage.setItem('userData', JSON.stringify(userData)); let walletLogo = "https://cdn.huddle.is/assets/img/eth-logo.png"; if (walletType === "sol") walletLogo = "https://cdn.huddle.is/assets/img/sol-logo.png"; else if (walletType === "abs") walletLogo = "https://cdn.huddle.is/assets/img/abs_logo.png"; const html = createWalletHTML(wallet, walletLogo, iconsHTML, walletType); $('#linked_wallets').append(html); $('#linked_wallet_area').show(); $('#fm_wallet_address').val(''); setTimeout(() => { if (typeof bootstrap !== 'undefined') { var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl); }); } }, 100); } updateData(); }, error: function () { $input.removeClass('input-loading'); } }); })); function formatPengu(pengu) { const num = parseFloat(pengu); if (num >= 1e6) return (num / 1e6).toFixed(1) + 'M'; if (num >= 1e3) return (num / 1e3).toFixed(1) + 'K'; return num.toString(); } function createWalletIcon(imgSrc, tooltip, radius) { return `
`; } function createTokenRow(address, img, name, featured, balance, price, link, curr) { var shorten_address = shortenAddress(address); var imageTag = ''; var tradeTag = ''; if (featured == true) { imageTag = `
`; tradeTag = `Trade`; }else{ imageTag = `
`; } return `
${imageTag}
${name}
${balance}
${tradeTag}
${price} USD
0 ${curr}
`; } function createNFTRow(address, img, name, featured, balance, price, link, verified, curr) { var shorten_address = shortenAddress(address); var imageTag = ''; var tradeTag = ''; var verifiedClass = ''; if (verified == false) { verifiedClass = ' unverified'; } if (featured == true) { imageTag = `
`; tradeTag = `Trade`; }else{ imageTag = `
`; } return `
${imageTag}
${name}
${balance}
${tradeTag}
${price} ETH
0 ${curr}
`; } function createWalletHTML(wallet, logoSrc, iconsHTML, walletType) { return `
${wallet.slice(0, 6)}...${wallet.slice(-4)}
${iconsHTML}
`; } //let addressData = userData.address; //let collections = userData.collections; //const $container = $("#linked_wallets"); //$container.empty(); const collectionLogos = { pudgypenguins: "0xbd3531da5cf5857e7cfaa92426877b022e612cf8.avif", lilpudgys: "0x524cab2ec69124574082676e6f654a18df49a048.avif", pudgyrods: "0x062e691c2054de82f28008a8ccc6d7a1c8ce060d.avif" }; let allWallets = new Set(); // Collect all wallet addresses from both sources (userData.address.eth || []).forEach(w => allWallets.add(w.wallet)); (userData.address.sol || []).forEach(w => allWallets.add(w.wallet)); Object.keys(userData.abs || {}).forEach(w => allWallets.add(w)); // Clear the container const $container = $("#linked_wallets"); $container.empty(); allWallets.forEach(wallet => { let walletType = 'eth'; let logoSrc = 'https://cdn.huddle.is/assets/img/eth-logo.png'; let balances = {}; let iconsHTML = ''; // Check if it's in Solana const solEntry = (userData.address.sol || []).find(w => w.wallet === wallet); if (solEntry) { walletType = 'sol'; balances = solEntry.balance || {}; logoSrc = 'https://cdn.huddle.is/assets/img/sol-logo.png'; if (balances.pengu) { iconsHTML += createWalletIcon( '/assets/img/' + collectionLogos.pudgypenguins, formatPengu(balances.pengu) + ' $PENGU', '50%' ); } } // Check if it's in Ethereum const ethEntry = (userData.address.eth || []).find(w => w.wallet === wallet); if (ethEntry) { balances = ethEntry.balance || {}; walletType = 'eth'; logoSrc = 'https://cdn.huddle.is/assets/img/eth-logo.png'; ["pudgypenguins", "lilpudgys", "pudgyrods"].forEach(function (key) { const count = parseInt(balances[key] || 0); if (count > 0) { const logo = `/assets/img/${collectionLogos[key]}`; iconsHTML += createWalletIcon(logo, count, "5px"); } }); if (balances.pengu) { iconsHTML += createWalletIcon( `/assets/img/${collectionLogos.pudgypenguins}`, formatPengu(balances.pengu) + ' $PENGU', "50%" ); } } // Check if it has Abstract holdings const absHoldings = userData.abs?.[wallet]; if (absHoldings) { const tokenCount = absHoldings.token?.length || 0; const nftCount = absHoldings.nft?.length || 0; if (tokenCount > 0) { iconsHTML += createWalletIcon( "/assets/img/vendor/abstract_logo_sq.png", `${tokenCount} tokens`, "50%" ); } if (nftCount > 0) { iconsHTML += createWalletIcon( "/assets/img/vendor/abstract_logo_sq.png", `${nftCount} collections`, "5px" ); } } const html = createWalletHTML(wallet, logoSrc, iconsHTML, walletType); $container.append(html); }); if ($container.children().length > 0) { $("#linked_wallet_area").show(); } else { $("#linked_wallet_area").hide(); } setTimeout(() => { if (typeof bootstrap !== 'undefined') { var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl); }); } }, 100); $(document).on('click', '.uil-times-circle', function () { const $walletEntry = $(this).closest('.wallet-entry'); const walletAddress = $walletEntry.data('wallet-address'); const walletType = $walletEntry.data('wallet-type'); let userData = JSON.parse(localStorage.getItem('userData')) || {}; if (userData.address && userData.address[walletType]) { const walletIndex = userData.address[walletType].findIndex(w => w.wallet === walletAddress); if (walletIndex > -1) { const walletObj = userData.address[walletType][walletIndex]; const balances = walletObj.balance || {}; if (balances.pengu && userData.token && userData.token.pengu) { userData.token.pengu.total -= parseInt(balances.pengu); if (userData.token.pengu.total < 0) userData.token.pengu.total = 0; } Object.keys(balances).forEach(key => { if (userData.collections && userData.collections[key]) { userData.collections[key].qty -= parseInt(balances[key]); if (userData.collections[key].qty < 0 || isNaN(userData.collections[key].qty)) { userData.collections[key].qty = 0; } } }); // NEW: Remove Abstract tokens if (walletType === 'eth' && userData.abs && userData.abs[walletAddress]) { delete userData.abs[walletAddress]; } userData.address[walletType].splice(walletIndex, 1); } localStorage.setItem('userData', JSON.stringify(userData)); updateData(); } $walletEntry.remove(); }); $(document).on('click', '.wallet-entry .uil-sync', function (event) { const refresh = !!event.originalEvent; const $icon = $(this); if ($icon.hasClass('is-rotating')) return; $icon.addClass('is-rotating').css('pointer-events', 'none'); const $entry = $(this).closest('.wallet-entry'); const walletAddress = $entry.data('wallet-address'); const walletType = $entry.data('wallet-type'); $.ajax({ url: '/ajax/wallet', method: 'POST', data: { huddle_xFkiLamdyJgamk: 'fb683d0ee1eb9e48ca04e1bda17edbea', address: walletAddress, walletType: walletType }, success: function (data) { const response = $.parseJSON(data); if (response.st === 1) { const result = response.return; if (userData.address[result.type]) { userData.address[result.type] = userData.address[result.type].filter(w => { if (w.wallet === result.wallet) { const balance = w.balance || {}; for (const [key, value] of Object.entries(balance)) { const qty = parseInt(value); if (key === 'pengu') { userData.token.pengu.total -= qty; if (userData.token.pengu.total < 0) userData.token.pengu.total = 0; } else if (userData.collections[key]) { userData.collections[key].qty -= qty; if (userData.collections[key].qty < 0) userData.collections[key].qty = 0; } } return false; } return true; }); } const balanceObj = {}; let iconsHTML = ''; let absTokens = []; let absNFTs = []; result.balances.forEach(bal => { const qty = parseFloat(bal.quantity); if (qty <= 0) return; if (bal.type === 'token') { balanceObj[bal.name] = qty; userData.token.pengu.total += qty; iconsHTML += createWalletIcon( "/assets/img/0xbd3531da5cf5857e7cfaa92426877b022e612cf8.avif", formatPengu(qty) + ' $PENGU', "50%" ); } else if (bal.type === 'nft') { if (userData.collections[bal.name]) { balanceObj[bal.name] = qty; userData.collections[bal.name].qty += qty; const contract = userData.collections[bal.name].contract_address; iconsHTML += createWalletIcon(`/assets/img/${contract}.avif`, qty, "5px"); } } else if (bal.type === 'abstoken') { absTokens.push({ c: bal.address, n: bal.name, b: qty }); } else if (bal.type === 'absnft') { absNFTs.push({ c: bal.address, n: bal.name, b: qty }); } }); // Store Abstract tokens & NFTs in abs if (walletType === 'abs' || absTokens.length > 0 || absNFTs.length > 0) { userData.abs[walletAddress] = { token: absTokens, nft: absNFTs }; // Append icons for Abstract (if any holdings) if (absTokens.length > 0) { iconsHTML += createWalletIcon( "/assets/img/vendor/abstract_logo_sq.png", `${absTokens.length} tokens`, "50%" ); } if (absNFTs.length > 0) { iconsHTML += createWalletIcon( "/assets/img/vendor/abstract_logo_sq.png", `${absNFTs.length} collections`, "5px" ); } } userData.address[result.type].push({ wallet: result.wallet, balance: balanceObj }); localStorage.setItem('userData', JSON.stringify(userData)); const updatedHTML = createWalletHTML(result.wallet, result.type === 'eth' ? 'https://cdn.huddle.is/assets/img/eth-logo.png' : result.type === 'sol' ? 'https://cdn.huddle.is/assets/img/sol-logo.png' : 'https://cdn.huddle.is/assets/img/abs-logo.png', iconsHTML, result.type ); $entry.replaceWith(updatedHTML); updateData(refresh); setTimeout(() => { if (typeof bootstrap !== 'undefined') { var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl); }); } }, 100); } }, complete: function () { $icon.removeClass('is-rotating').css('pointer-events', ''); } }); }); function applyDarkMode(isDark) { $('body').toggleClass('dark-mode', isDark); const $icon = $('#toggleDarkMode'); $icon.removeClass('uil-moon uil-sun'); $icon.addClass(isDark ? 'uil-sun' : 'uil-moon'); updateStatusBarStyle(isDark); } function capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); } function toggleGroupExpansion(groupKey, $toggleIcon, $contentBlock) { const isExpanded = $toggleIcon.hasClass('uil-angle-down'); $toggleIcon.toggleClass('uil-angle-down uil-angle-up'); $contentBlock.slideToggle(200); updateSetting(groupKey, !isExpanded); // Save to userData.settings } $('.hm-group-title .collapse-toggle').on('click', function () { const $icon = $(this).find('i'); const $title = $(this).closest('.hm-group-title'); const groupClass = $title.attr('class').split(/\s+/).find(c => c !== 'hm-group-title'); const groupKey = `exp${capitalize(groupClass)}Group`; const $content = $(`.group-content[data-group="${groupClass}"]`); toggleGroupExpansion(groupKey, $icon, $content); }); function populateABSData(absData) { if (!absData || Object.keys(absData).length === 0) { return; } const settings = userData.settings; const tokenAgg = aggregateByContract(absData, "token"); const nftAgg = aggregateByContract(absData, "nft"); insertTokenHTML(tokenAgg); insertNFTHTML(nftAgg); setTimeout(() => { applyMasking(settings.isMasked); }, 900); } function aggregateByContract(absData, kind) { const map = {}; Object.keys(absData).forEach(wallet => { const list = absData[wallet]?.[kind]; if (!Array.isArray(list)) return; list.forEach(entry => { const contract = String(entry.c || "").toLowerCase(); if (!contract) return; const name = entry.n ?? ""; const bal = Number(entry.b ?? 0) || 0; if (!map[contract]) { map[contract] = { contract, name, totalBalance: 0 }; } map[contract].totalBalance += bal; map[contract].name = name || map[contract].name; }); }); return map; } function shortenAddress(addr) { return String(addr || "").replace(/^0x/i, "").toLowerCase(); } function setAmount(spanEl, amount) { const val = Math.round(Number(amount) || 0); // round to integer if (!spanEl) return; spanEl.dataset.original = String(val); spanEl.textContent = val.toLocaleString(); // adds thousand separators } function insertTokenHTML(popData) { // console.log(`TOKEN | Contract: ${contract}, Name: ${name}, Balance: ${balance}`); $.ajax({ url: '/ajax/populate', method: 'POST', data: { huddle_xFkiLamdyJgamk:'fb683d0ee1eb9e48ca04e1bda17edbea', data: popData, type: 'token', }, success: function (data) { var response = jQuery.parseJSON(data); if (response.st == 1) { var tokens = Array.isArray(response.tokens) ? response.tokens : (Array.isArray(response) ? response : []); var listBody = document.querySelector('#abstoken .list-body'); if (!listBody) return; listBody.innerHTML = ''; tokens.forEach(function (t) { var address = t.address || ''; var name = t.name || ''; var img = t.img || '/assets/img/vendor/abstract_logo_default.png'; var price = t.price != null ? String(t.price) : ''; var link = t.link || '#'; var curr = ((userData?.settings?.priceAs == 'crypto')?'ETH':userData?.settings?.currency); var featured = !!t.featured; var shorten = shortenAddress(address); var rowId = 'row_' + shorten; var amountId = 'token_amount_' + shorten; var bal = t.balance; var row = document.getElementById(rowId); // Create new row (featured keeps link + Trade; general has neither) var html = createTokenRow(address, img, name, featured, bal, price, link, curr); var temp = document.createElement('div'); temp.innerHTML = html.trim(); var newRow = temp.firstElementChild; // set data-original for downstream scripts var amountSpanNew = newRow.querySelector('#' + CSS.escape(amountId)); if (amountSpanNew) { amountSpanNew.dataset.original = bal; } listBody.appendChild(newRow); }); setTimeout(() => { if (typeof bootstrap !== 'undefined') { var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl); }); } }, 100); } }, error: function () { $dataElements.removeClass('skeleton-bg'); } }); } function insertNFTHTML(popData) { // console.log(`NFT | Contract: ${contract}, Name: ${name}, Balance: ${balance}`); $.ajax({ url: '/ajax/populate', method: 'POST', data: { huddle_xFkiLamdyJgamk:'fb683d0ee1eb9e48ca04e1bda17edbea', data: popData, type: 'nft', }, success: function (data) { var response = jQuery.parseJSON(data); if (response.st == 1) { var nfts = Array.isArray(response.nfts) ? response.nfts : (Array.isArray(response) ? response : []); var listBody = document.querySelector('#absnft .list-body'); if (!listBody) return; listBody.innerHTML = ''; nfts.forEach(function (t) { var address = t.address || ''; var name = t.name || ''; var slug = t.slug || ''; var img = t.img || '/assets/img/vendor/abstract_logo_default.png'; var price = t.price != null ? String(t.price) : ''; var link = t.link || '#'; var featured = !!t.featured; var verified = t.verified || ''; var curr = ((userData?.settings?.priceAs == 'crypto')?'ETH':userData?.settings?.currency); var shorten = shortenAddress(address); var rowId = 'row_' + shorten; var amountId = 'nft_amount_' + shorten; var bal = t.balance; var row = document.getElementById(rowId); // Create new row (featured keeps link + Trade; general has neither) var html = createNFTRow(address, img, name, featured, bal, price, link, verified, curr); var temp = document.createElement('div'); temp.innerHTML = html.trim(); var newRow = temp.firstElementChild; // set data-original for downstream scripts var amountSpanNew = newRow.querySelector('#' + CSS.escape(amountId)); if (amountSpanNew) { amountSpanNew.dataset.original = bal; } listBody.appendChild(newRow); }); setTimeout(() => { if (typeof bootstrap !== 'undefined') { var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl); }); } }, 100); } }, error: function () { $dataElements.removeClass('skeleton-bg'); } }); } function finalizeFeaturedVisibility() { const rows = document.querySelectorAll('#abstoken .list-body .list-body-row'); rows.forEach(row => { // Heuristic to detect "featured": link around image or Trade badge present const hasImgLink = !!row.querySelector('.collection-token-img a'); const hasTradeBadge = !!row.querySelector('.badge.gradient-5.rounded-pill'); const isFeatured = hasImgLink || hasTradeBadge; if (!isFeatured) return; // only hide/show featured, leave general rows alone // Find the amount span (id starts with token_amount_) const amountSpan = row.querySelector('span[id^="token_amount_"]'); const val = Number(amountSpan?.dataset?.original ?? amountSpan?.textContent ?? '0') || 0; if (val > 0) { row.classList.remove('d-none'); row.style.display = ''; } else { // Hide featured tokens not currently held row.classList.add('d-none'); } }); } function updateStatusBarStyle(darkModeEnabled) { let metaTag = document.querySelector("meta[name='apple-mobile-web-app-status-bar-style']"); if (!metaTag) { metaTag = document.createElement('meta'); metaTag.name = "apple-mobile-web-app-status-bar-style"; document.head.appendChild(metaTag); } metaTag.content = darkModeEnabled ? 'black-translucent' : 'default'; } $('#toggleDarkMode').on('click', function () { const isCurrentlyDark = $('body').hasClass('dark-mode'); const newState = !isCurrentlyDark; applyDarkMode(newState); updateSetting('darkMode', newState); }); $(window).on('beforeunload', stopPolling); function hasHuddle() { const targetTokenAddress = "0x143bd0eac0a811aac43e8aae5d28b624b6b63489"; try { const userData = JSON.parse(localStorage.getItem('userData')); if (!userData || !userData.abs) return false; const absWallets = userData.abs; return Object.values(absWallets).some(wallet => { const tokens = wallet.token || []; return tokens.some(token => token.c === targetTokenAddress); }); } catch (e) { console.error('Error checking huddle token ownership:', e); return false; } } var preloader = document.querySelector('.page-loader'); if(preloader != null) { document.body.onload = function(){ setTimeout(function() { if( !preloader.classList.contains('done') ) { preloader.classList.add('done'); } }, waitTime) } } });