(function () { var viewport = document.getElementById('appViewport'); var profileName = document.querySelector('.profile-name'); var profileID = document.querySelector('.profile-id'); var profileRecharge = document.querySelector('.profile-recharge'); var profileAvatar = document.querySelector('.profile-avatar'); var stage = document.querySelector('.stage'); var stageWrap = document.getElementById('stageWrap'); var rewardSectionRoot = document.getElementById('rewardSections'); var bgBottom = document.querySelector('.bg-bottom'); var countNodes = { days: document.querySelector('[data-count="days"]'), hours: document.querySelector('[data-count="hours"]'), minutes: document.querySelector('[data-count="minutes"]'), seconds: document.querySelector('[data-count="seconds"]'), }; var DESIGN_WIDTH = 1080; var designHeight = 3600; var VIEWPORT_WIDTH = 375; var STORAGE_KEY = 'hy_recharge_reward_end_at'; var timeOffsetMS = 0; var countdownEndAt = 0; var countdownTimer = 0; var fallbackIcons = [ 'reward-ring-blue.png', 'reward-ring-white.png', 'reward-ring-gold.png', 'reward-ring-purple.png', 'reward-ring-amber.png', ]; var fallbackSections = [ { thresholdUsdMinor: 10000, cards: [ { label: '$100', icon: 'reward-ring-blue.png', name: 'Frame' }, { label: '$100', icon: 'reward-ring-white.png', name: 'Avatar frame' }, ], }, { thresholdUsdMinor: 10000, cards: [ { label: '$100', icon: 'reward-ring-gold.png', name: 'Gold frame' }, { label: '$100', icon: 'reward-ring-purple.png', name: 'Purple frame' }, { label: '$100', icon: 'reward-ring-amber.png', name: 'Amber frame' }, ], }, { thresholdUsdMinor: 20000, cards: [ { label: '$200', icon: 'reward-ring-blue.png', name: 'Blue frame' }, { label: '$200', icon: 'reward-ring-white.png', name: 'White frame' }, { label: '$200', icon: 'reward-ring-purple.png', name: 'Purple frame' }, { label: '$200', icon: 'reward-ring-amber.png', name: 'Amber frame' }, ], }, ]; function getParams() { return new URLSearchParams(window.location.search || ''); } function t(key, fallback) { return window.HyAppI18n && window.HyAppI18n.t ? window.HyAppI18n.t(key, fallback) : fallback || key; } function isMock(params) { var value = params.get('mock'); return value === '1' || value === 'true' || !window.HyAppAPI; } function twoDigits(value) { return String(Math.max(0, value)).padStart(2, '0'); } function formatUSDMinor(value) { return ( '$' + (Number(value || 0) / 100).toLocaleString('en-US', { maximumFractionDigits: 2, minimumFractionDigits: Number(value || 0) % 100 === 0 ? 0 : 2, }) ); } function parseAmountToMinor(value, fallbackMinor) { var amount = Number(String(value || '').replace(/[^\d.]/g, '')); if (!Number.isFinite(amount)) return fallbackMinor; return Math.round(amount * 100); } function serverNow() { return Date.now() - timeOffsetMS; } function resolveFallbackEndAt(params) { var explicitEnd = Date.parse(params.get('end_at') || params.get('endAt') || ''); if (Number.isFinite(explicitEnd) && explicitEnd > Date.now()) return explicitEnd; var cached = Number(window.localStorage.getItem(STORAGE_KEY)); if (Number.isFinite(cached) && cached > Date.now()) return cached; var fallback = Date.now() + 25 * 60 * 60 * 1000 + 61 * 1000; window.localStorage.setItem(STORAGE_KEY, String(fallback)); return fallback; } function updateCountdown() { var diff = Math.max(0, countdownEndAt - serverNow()); var totalSeconds = Math.floor(diff / 1000); var days = Math.floor(totalSeconds / 86400); var hours = Math.floor((totalSeconds % 86400) / 3600); var minutes = Math.floor((totalSeconds % 3600) / 60); var seconds = totalSeconds % 60; countNodes.days.textContent = twoDigits(days); countNodes.hours.textContent = twoDigits(hours); countNodes.minutes.textContent = twoDigits(minutes); countNodes.seconds.textContent = twoDigits(seconds); } function applyProfile(data) { var user = data.user || {}; var displayUserID = user.display_user_id || user.displayUserId || user.user_id || user.userId || '12345678'; var name = user.username || user.name || 'nanfangjian'; var avatar = user.avatar || ''; var recharge = data.progress ? data.progress.total_usd_minor || data.progress.totalUsdMinor : data.totalUsdMinor; profileName.textContent = name; profileID.textContent = t('rechargeReward.idPrefix', 'ID:') + ' ' + displayUserID; profileRecharge.textContent = t('rechargeReward.accumulatedRecharge', 'Accumulated Recharge:') + formatUSDMinor(recharge || 0); if (avatar) profileAvatar.src = avatar; } function updateBodyHeight() { var scale = Math.min(window.innerWidth / VIEWPORT_WIDTH, 1); document.documentElement.style.setProperty('--app-scale', String(scale)); stageWrap.style.height = Math.ceil(designHeight * (VIEWPORT_WIDTH / DESIGN_WIDTH)) + 'px'; document.body.style.minHeight = viewport.offsetHeight * scale + 'px'; } function cardLayout(count, index) { var compact = count >= 4; var small = count === 3; var layouts = compact ? [ { left: 105, top: 178 }, { left: 337, top: 178 }, { left: 569, top: 178 }, { left: 801, top: 178 }, ] : small ? [ { left: 142, top: 189 }, { left: 423, top: 189 }, { left: 704, top: 189 }, ] : [ { left: 152, top: 189 }, { left: 601, top: 189 }, ]; return layouts[index] || layouts[layouts.length - 1]; } function sectionMetrics(index, count) { var firstTop = 1693; if (index === 0) { return { top: firstTop, panelTop: 83, panelClass: 'reward-panel-large', cardClass: count >= 4 ? 'reward-card-compact' : 'reward-card-large', }; } return { top: firstTop + 623 + (index - 1) * 512, panelTop: 10, panelClass: 'reward-panel-small', cardClass: count >= 4 ? 'reward-card-compact' : 'reward-card-small', }; } function normalizeCard(item, tier, index) { var resource = item && item.resource ? item.resource : {}; var icon = resource.preview_url || resource.previewUrl || resource.asset_url || resource.assetUrl || ''; return { label: formatUSDMinor(tier.thresholdUsdMinor || tier.threshold_usd_minor || 0), icon: icon || fallbackIcons[index % fallbackIcons.length], name: resource.name || item.item_type || item.itemType || tier.tierName || tier.tier_name || 'Reward', granted: Boolean(tier.grant && tier.grant.status === 'granted'), remoteIcon: Boolean(icon), }; } function normalizeTier(tier, index) { var resourceGroup = tier.resource_group || tier.resourceGroup || {}; var items = Array.isArray(resourceGroup.items) ? resourceGroup.items : []; var fallback = fallbackSections[index % fallbackSections.length]; var normalized = { thresholdUsdMinor: Number(tier.threshold_usd_minor || tier.thresholdUsdMinor || fallback.thresholdUsdMinor || 0) || 0, tierName: tier.tier_name || tier.tierName || '', grant: tier.grant || null, cards: [], }; normalized.cards = items.length ? items.slice(0, 4).map(function (item, cardIndex) { return normalizeCard(item, normalized, cardIndex); }) : fallback.cards.map(function (card) { return { label: formatUSDMinor(normalized.thresholdUsdMinor || fallback.thresholdUsdMinor), icon: card.icon, name: card.name, granted: Boolean(normalized.grant && normalized.grant.status === 'granted'), }; }); return normalized; } function renderRewardCard(card, cardClass, count, index) { var frame = cardClass === 'reward-card-large' ? 'reward-card-frame-large.png' : 'reward-card-frame.png'; var pos = cardLayout(count, index); var iconSrc = card.remoteIcon ? card.icon : './assets/' + card.icon; return [ '
', '', '', '', escapeHTML(card.label), '', '
', ].join(''); } function renderRewardSections(tiers) { var sections = (tiers && tiers.length ? tiers : fallbackSections).map(normalizeTier); rewardSectionRoot.innerHTML = sections .map(function (section, index) { var count = Math.max(1, section.cards.length); var metrics = sectionMetrics(index, count); return [ '
', '
', '', '', escapeHTML( t('rechargeReward.rechargeTitle', 'Recharge {amount}').replace( '{amount}', formatUSDMinor(section.thresholdUsdMinor) ) ), '', '
', '
', '', '
', section.cards .map(function (card, cardIndex) { return renderRewardCard(card, metrics.cardClass, count, cardIndex); }) .join(''), '
', ].join(''); }) .join(''); var lastMetrics = sectionMetrics(sections.length - 1, sections[sections.length - 1].cards.length); designHeight = Math.max(3600, lastMetrics.top + 660); stage.style.height = designHeight + 'px'; if (bgBottom) bgBottom.style.top = Math.max(3022, designHeight - 578) + 'px'; updateBodyHeight(); } function mockData(params) { var total = parseAmountToMinor(params.get('recharge'), 599800); var endAt = resolveFallbackEndAt(params); return Promise.resolve({ user: { username: params.get('name') || 'nanfangjian', display_user_id: params.get('uid') || params.get('id') || '12345678', avatar: params.get('avatar') || './assets/profile-avatar.png', }, progress: { total_usd_minor: total }, period: { end_ms: endAt }, tiers: fallbackSections.map(function (section, index) { return { threshold_usd_minor: section.thresholdUsdMinor, tier_name: 'Tier ' + (index + 1), grant: total >= section.thresholdUsdMinor ? { status: 'granted' } : null, resource_group: { items: [] }, }; }), server_time_ms: Date.now(), }); } function fetchStatus(params) { if (isMock(params)) return mockData(params); return window.HyAppAPI.get('/api/v1/activities/cumulative-recharge-reward').catch(function (error) { console.warn('cumulative recharge reward status failed', error); return mockData(params); }); } function applyData(data, params) { data = data || {}; timeOffsetMS = Date.now() - Number(data.server_time_ms || data.serverTimeMs || Date.now()); countdownEndAt = Number((data.period && (data.period.end_ms || data.period.endMs)) || data.period_end_ms || 0) || resolveFallbackEndAt(params); applyProfile(data); renderRewardSections(data.tiers || []); updateCountdown(); if (countdownTimer) window.clearInterval(countdownTimer); countdownTimer = window.setInterval(updateCountdown, 1000); } function escapeHTML(value) { return String(value || '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function init() { var params = getParams(); document.title = t('rechargeReward.pageTitle', 'Recharge Reward'); fetchStatus(params).then(function (data) { applyData(data, params); }); window.addEventListener('resize', updateBodyHeight); window.addEventListener('hyapp:i18n-ready', function () { document.title = t('rechargeReward.pageTitle', 'Recharge Reward'); fetchStatus(params).then(function (data) { applyData(data, params); }); }); } init(); })();