2026-07-07 18:02:14 +08:00

205 lines
7.4 KiB
JavaScript

(function () {
var ASSET_BASE = './assets/recharge-activity/';
var viewport = document.getElementById('appViewport');
var rewardSections = document.getElementById('rewardSections');
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 DESIGN_HEIGHT = 3273;
var VIEWPORT_WIDTH = 375;
var countdownEndAt = resolveCountdownEndAt();
var activityData = {
user: {
name: 'nanfangjian',
id: '12345678',
avatar: ASSET_BASE + 'profile-avatar.png',
accumulatedRecharge: '5998',
},
rewards: [
{
amount: '$100',
top: 1776,
background: ASSET_BASE + 'reward-section-a.png',
claim: { text: 'Receive', claimable: true, claimed: false },
cards: [
{ price: '$100', icon: ASSET_BASE + 'reward-icon.png', left: 111, top: 293 },
{ price: '$100', icon: ASSET_BASE + 'reward-icon.png', left: 560, top: 293 },
],
},
{
amount: '$100',
top: 2466,
background: ASSET_BASE + 'reward-section-b.png',
claim: { text: 'Receive', claimable: true, claimed: false },
cards: [
{ price: '$100', icon: ASSET_BASE + 'reward-icon.png', left: 70.5, top: 278 },
{ price: '$100', icon: ASSET_BASE + 'reward-icon.png', left: 339.5, top: 278 },
{ price: '$100', icon: ASSET_BASE + 'reward-icon.png', left: 608.5, top: 278 },
],
},
],
};
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 escapeHTML(value) {
return String(value || '')
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function twoDigits(value) {
return String(Math.max(0, value)).padStart(2, '0');
}
function resolveCountdownEndAt() {
var params = getParams();
var explicitEnd = Date.parse(params.get('end_at') || params.get('endAt') || '');
if (Number.isFinite(explicitEnd) && explicitEnd > Date.now()) return explicitEnd;
var hours = Number(params.get('hours'));
if (Number.isFinite(hours) && hours > 0) return Date.now() + hours * 60 * 60 * 1000;
// This page is intentionally interface-free for now; when an API is added,
// use the server period end time here and do not recover failed requests with mock data.
return Date.now() + 25 * 60 * 60 * 1000 + 61 * 1000;
}
function updateScale() {
var scale = Math.min(window.innerWidth / VIEWPORT_WIDTH, 1);
var scaledHeight = Math.ceil(DESIGN_HEIGHT * (VIEWPORT_WIDTH / DESIGN_WIDTH));
document.documentElement.style.setProperty('--app-scale', String(scale));
viewport.style.minHeight = scaledHeight + 'px';
document.body.style.minHeight = Math.ceil(scaledHeight * scale) + 'px';
}
function updateCountdown() {
var diff = Math.max(0, countdownEndAt - Date.now());
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 renderProfile(user) {
document.querySelector('[data-profile-avatar]').src = user.avatar;
document.querySelector('[data-profile-name]').textContent = user.name;
document.querySelector('[data-profile-id]').textContent =
t('rechargeReward.idPrefix', 'ID:') + ' ' + user.id;
document.querySelector('[data-profile-recharge]').textContent =
t('rechargeReward.accumulatedRecharge', 'Accumlated Recharge:') +
user.accumulatedRecharge;
}
function renderRewardCard(card) {
return [
'<div class="reward-card" style="left:',
card.left,
'px;top:',
card.top,
'px">',
'<img class="reward-frame" src="',
ASSET_BASE,
'reward-card-frame.png',
'" alt="" />',
'<img class="reward-icon" src="',
escapeHTML(card.icon),
'" alt="" />',
'<span class="reward-price">',
escapeHTML(card.price),
'</span>',
'</div>',
].join('');
}
function renderClaimButton(claim) {
var stateClass = claim.claimed ? ' is-claimed' : claim.claimable ? ' is-claimable' : ' is-disabled';
return [
'<button class="claim-button',
stateClass,
'" type="button"',
claim.claimable && !claim.claimed ? '' : ' disabled',
'>',
'<img class="claim-button-bg" src="',
ASSET_BASE,
'receive-button-bg.png" alt="" />',
'<span>',
escapeHTML(claim.text || 'Receive'),
'</span>',
'</button>',
].join('');
}
function renderRewards(rewards) {
rewardSections.innerHTML = rewards
.map(function (section) {
var title = t('rechargeReward.rechargeTitle', 'Recharge {amount}').replace(
'{amount}',
section.amount
);
return [
'<section class="reward-section" style="--section-top:',
section.top,
'px">',
'<img class="reward-section-bg" src="',
escapeHTML(section.background),
'" alt="" />',
'<div class="title-pill">',
'<span>',
escapeHTML(title),
'</span>',
'</div>',
section.cards
.map(function (card) {
return renderRewardCard(card);
})
.join(''),
renderClaimButton(section.claim || { text: 'Receive', claimable: false }),
'</section>',
].join('');
})
.join('');
}
function renderPage() {
document.title = t('rechargeReward.pageTitle', 'Recharge Reward');
renderProfile(activityData.user);
renderRewards(activityData.rewards);
}
function init() {
updateScale();
updateCountdown();
renderPage();
window.setInterval(updateCountdown, 1000);
window.addEventListener('resize', updateScale);
window.addEventListener('hyapp:i18n-ready', renderPage);
}
init();
})();