369 lines
14 KiB
JavaScript
369 lines
14 KiB
JavaScript
(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 [
|
|
'<div class="reward-card ',
|
|
cardClass,
|
|
card.granted ? ' is-granted' : '',
|
|
'" data-reward-card="',
|
|
escapeHTML(card.name),
|
|
'" style="left:',
|
|
pos.left,
|
|
'px;top:',
|
|
pos.top,
|
|
'px">',
|
|
'<img class="reward-frame" src="./assets/',
|
|
frame,
|
|
'" alt="" />',
|
|
'<img class="reward-icon" src="',
|
|
escapeHTML(iconSrc),
|
|
'" alt="" />',
|
|
'<span class="reward-price">',
|
|
escapeHTML(card.label),
|
|
'</span>',
|
|
'</div>',
|
|
].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 [
|
|
'<section class="reward-section" style="--section-top:',
|
|
metrics.top,
|
|
'px">',
|
|
'<div class="title-pill" aria-label="Recharge ',
|
|
formatUSDMinor(section.thresholdUsdMinor),
|
|
' rewards">',
|
|
'<img src="./assets/section-title-frame.png" alt="" />',
|
|
'<span>',
|
|
escapeHTML(
|
|
t('rechargeReward.rechargeTitle', 'Recharge {amount}').replace(
|
|
'{amount}',
|
|
formatUSDMinor(section.thresholdUsdMinor)
|
|
)
|
|
),
|
|
'</span>',
|
|
'</div>',
|
|
'<div class="reward-panel ',
|
|
metrics.panelClass,
|
|
'" style="--panel-top:',
|
|
metrics.panelTop,
|
|
'px">',
|
|
'<img class="reward-panel-frame" src="./assets/reward-panel-frame.png" alt="" />',
|
|
'</div>',
|
|
section.cards
|
|
.map(function (card, cardIndex) {
|
|
return renderRewardCard(card, metrics.cardClass, count, cardIndex);
|
|
})
|
|
.join(''),
|
|
'</section>',
|
|
].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, '"')
|
|
.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();
|
|
})();
|