2026-06-09 13:07:31 +08:00

413 lines
16 KiB
JavaScript

(function () {
var page = document.getElementById('page');
var tabs = Array.prototype.slice.call(document.querySelectorAll('[data-tab-target]'));
var panels = Array.prototype.slice.call(document.querySelectorAll('[data-panel]'));
var stageWrap = document.getElementById('stageWrap');
var dateText = document.querySelector('.date-text');
var note = document.querySelector('.note');
var historyDate = document.querySelector('.history-date');
var COIN_IMAGE = './assets/coin.png';
var mockRanks = [
{ rank: 1, name: 'hanneem', score: '337700', avatar: './assets/avatar-top.png' },
{ rank: 2, name: 'hanneem', score: '337700', avatar: './assets/avatar-user.png' },
{ rank: 3, name: 'hanneem', score: '337700', avatar: './assets/avatar-user.png' },
{ rank: 4, name: 'hanneem', score: '337700', avatar: './assets/avatar-user.png' },
{ rank: 5, name: 'hanneem', score: '337700', avatar: './assets/avatar-user.png' },
];
var mockRewards = [
{ rank_no: 1, resource_group_id: 1001, top: 1488 },
{ rank_no: 2, resource_group_id: 1002, top: 1985 },
{ rank_no: 3, resource_group_id: 1003, top: 2482 },
];
var state = {
current: null,
weeklyEntries: [],
historyCycles: [],
error: '',
loading: true,
};
function rankMedal(rank) {
if (rank === 1) return './assets/medal-1.png';
if (rank === 2) return './assets/medal-2.png';
if (rank === 3) return './assets/medal-3.png';
return '';
}
function renderRankList(targetID, top, items, emptyText) {
var list = document.getElementById(targetID);
var rows = (items || []).slice(0, 8);
if (!rows.length) {
list.innerHTML =
'<div class="rank-empty" style="top:' + top + 'px">' + escapeHTML(emptyText || 'No ranking data yet.') + '</div>';
return;
}
list.innerHTML = rows
.map(function (item, index) {
var medal = rankMedal(item.rank);
return [
'<article class="rank-row ',
index % 2 === 0 ? 'is-odd' : 'is-even',
'" style="top:',
top + index * 140,
'px">',
medal
? '<img class="rank-medal" src="' + medal + '" alt="No. ' + item.rank + '" />'
: '<span class="rank-number">' + item.rank + '</span>',
'<img class="rank-avatar" src="' + escapeAttr(item.avatar || './assets/avatar-user.png') + '" alt="" />',
'<div class="rank-name">',
escapeHTML(item.name || '-'),
'</div>',
'<div class="rank-score">',
escapeHTML(formatNumber(item.score)),
'</div>',
'</article>',
].join('');
})
.join('');
}
function rewardItem(item) {
var img = item.image || './assets/gift-ball.png';
var label = item.label || 'Resource Group #' + (item.resource_group_id || '-');
var labelHTML = item.coin ? '<div class="reward-label">' + escapeHTML(label) + '</div>' : '';
return [
'<div class="reward-item',
item.single ? ' is-single' : '',
item.coin ? ' is-coin' : '',
'">',
'<div class="gift-card">',
'<img class="gift-frame" src="./assets/gift-card.png" alt="" />',
'<img class="gift-glow" src="./assets/gift-glow.png" alt="" />',
'<img class="gift-img" src="' + escapeAttr(img) + '" alt="Reward" />',
'</div>',
labelHTML,
'</div>',
].join('');
}
function renderRewards(rewards) {
var root = document.getElementById('rewardGroups');
var groups = normalizeRewards(rewards);
root.innerHTML = groups
.map(function (group) {
return [
'<section class="reward-group" style="top:',
group.top,
'px">',
'<h2 class="reward-title">',
'<img src="./assets/reward-title-bar.png" alt="" />',
'<span>TOP',
group.rank_no,
'</span>',
'</h2>',
group.items.map(rewardItem).join(''),
'</section>',
].join('');
})
.join('');
}
function normalizeRewards(rewards) {
var tops = [1488, 1985, 2482];
var input = rewards && rewards.length ? rewards : [];
return [1, 2, 3].map(function (rankNo, index) {
var reward =
input.find(function (item) {
return Number(item.rank_no || item.rankNo) === rankNo;
}) || {};
var items = normalizeRewardItems(reward);
if (!items.length) {
items = [
{
label: reward.name || 'Resource Group #' + (reward.resource_group_id || reward.resourceGroupId || '-'),
resource_group_id: reward.resource_group_id || reward.resourceGroupId,
image: reward.image_url || reward.imageUrl || reward.icon_url || reward.iconUrl,
single: true,
},
];
}
return { rank_no: rankNo, top: tops[index], items: items.slice(0, 3) };
});
}
function normalizeRewardItems(reward) {
var group = reward.resource_group || reward.resourceGroup || reward.reward_resource_group || reward.rewardResourceGroup || {};
var items = reward.items || reward.resources || group.items || [];
return (items || []).map(function (item) {
var resource = item.resource || {};
var quantity = Number(item.quantity || 0);
var walletAmount = Number(item.wallet_asset_amount || item.walletAssetAmount || 0);
var walletType = String(item.wallet_asset_type || item.walletAssetType || '').toUpperCase();
var name =
item.label ||
resource.name ||
item.name ||
(walletType ? walletType.replace(/_/g, ' ') : '') ||
group.name ||
reward.name ||
'Reward';
var suffix = quantity > 1 ? ' x' + quantity : walletAmount > 0 ? ' x' + formatNumber(walletAmount) : '';
return {
label: name + suffix,
resource_group_id: reward.resource_group_id || reward.resourceGroupId || group.group_id || group.groupId,
coin: walletType === 'COIN',
image:
walletType === 'COIN'
? COIN_IMAGE
: item.image ||
item.image_url ||
item.imageUrl ||
resource.preview_url ||
resource.previewUrl ||
resource.asset_url ||
resource.assetUrl ||
resource.animation_url ||
resource.animationUrl,
single: false,
};
});
}
function renderGiftStrip(gifts) {
var cards = Array.prototype.slice.call(document.querySelectorAll('.gift-strip .gift-card'));
cards.forEach(function (card, index) {
var gift = (gifts || [])[index] || {};
var resource = gift.resource || {};
var img =
gift.image_url ||
gift.imageUrl ||
gift.cover_url ||
gift.coverUrl ||
gift.preview_url ||
gift.previewUrl ||
gift.icon_url ||
gift.iconUrl ||
resource.preview_url ||
resource.previewUrl ||
gift.asset_url ||
gift.assetUrl ||
resource.asset_url ||
resource.assetUrl;
var image = card.querySelector('.gift-img');
if (image && img) image.src = img;
var labelNode = card.querySelector('.gift-name');
if (labelNode) {
labelNode.remove();
}
});
}
function renderActivity() {
var cycle = state.current && state.current.cycle;
var historyCycle = state.historyCycles[0] || {};
var historyEntries = normalizeEntries(historyCycle.top_entries || historyCycle.topEntries || []);
var weeklyEntries = normalizeEntries(state.weeklyEntries);
if (cycle) {
dateText.textContent = formatRangeUTC(cycle.start_ms || cycle.startMs, cycle.end_ms || cycle.endMs);
note.innerHTML =
'Note: Send the following three gifts to participate in the<br />ranking. Users who rank in the top 3 of the leaderboard<br />will receive rewards.';
renderGiftStrip(cycle.gifts || []);
renderRewards(cycle.rewards || []);
} else {
dateText.textContent = 'No active cycle';
note.textContent = state.error || 'No Weekly Star activity is configured for your region.';
renderGiftStrip([]);
renderRewards([]);
}
if (historyCycle.cycle) {
historyDate.textContent = formatDateRange(historyCycle.cycle.start_ms || historyCycle.cycle.startMs, historyCycle.cycle.end_ms || historyCycle.cycle.endMs);
} else {
historyDate.textContent = 'Previous Top 3 Records';
}
renderRankList('historyList', 1607, historyEntries, 'No previous ranking records.');
renderRankList('weeklyList', 1496, weeklyEntries, state.loading ? 'Loading...' : 'No ranking data yet.');
}
function normalizeEntries(entries) {
return (entries || []).map(function (entry, index) {
var user = entry.user || {};
return {
rank: Number(entry.rank_no || entry.rankNo || index + 1),
name: user.username || user.display_user_id || user.displayUserId || entry.user_id || entry.userId || '-',
score: entry.score || 0,
avatar: user.avatar || (Number(entry.rank_no || entry.rankNo) === 1 ? './assets/avatar-top.png' : './assets/avatar-user.png'),
};
});
}
function setTab(tabName) {
var next = tabName || 'history';
page.setAttribute('data-tab', next);
tabs.forEach(function (tab) {
var active = tab.getAttribute('data-tab-target') === next;
tab.classList.toggle('is-active', active);
tab.setAttribute('aria-selected', active ? 'true' : 'false');
});
panels.forEach(function (panel) {
panel.hidden = panel.getAttribute('data-panel') !== next;
});
updateBodyHeight();
}
function normalizeTab(value) {
if (value === 'weekly' || value === 'rewards' || value === 'history') return value;
return 'history';
}
function updateBodyHeight() {
var scale = window.innerWidth / 1080;
var designHeight = page.getAttribute('data-tab') === 'rewards' ? 3138 : 2310;
document.documentElement.style.setProperty('--design-scale', String(scale));
stageWrap.style.height = Math.ceil(designHeight * scale) + 'px';
document.body.style.minHeight = stageWrap.offsetHeight + 'px';
}
function loadMock() {
state.loading = false;
state.current = {
cycle: {
start_ms: Date.UTC(2025, 11, 28, 0, 0, 0),
end_ms: Date.UTC(2026, 0, 3, 23, 59, 0),
gifts: [{ gift_id: 'gift_1' }, { gift_id: 'gift_2' }, { gift_id: 'gift_3' }],
rewards: mockRewards,
},
};
state.weeklyEntries = mockRanks.map(function (item) {
return {
rank_no: item.rank,
score: item.score,
user: { username: item.name, avatar: item.avatar },
};
});
state.historyCycles = [
{
cycle: {
start_ms: Date.UTC(2025, 11, 7, 0, 0, 0),
end_ms: Date.UTC(2025, 11, 14, 23, 59, 0),
},
top_entries: state.weeklyEntries.slice(0, 3),
},
];
renderActivity();
}
function loadReal() {
if (!window.HyAppAPI || !window.HyAppAPI.weeklyStar) {
state.loading = false;
state.error = 'API module is not available.';
renderActivity();
return;
}
renderRankList('weeklyList', 1496, [], 'Loading...');
window.HyAppAPI.weeklyStar
.current()
.then(function (current) {
state.current = current || {};
var cycle = state.current.cycle || {};
var cycleID = cycle.cycle_id || cycle.cycleId || '';
return Promise.all([
cycleID
? window.HyAppAPI.weeklyStar.leaderboard({ cycle_id: cycleID, page_size: 20 })
: Promise.resolve({ entries: [] }),
window.HyAppAPI.weeklyStar.history(10),
]);
})
.then(function (results) {
state.weeklyEntries = results[0].entries || [];
state.historyCycles = results[1].cycles || [];
state.error = '';
})
.catch(function (error) {
state.error = error.message || 'Unable to load Weekly Star.';
state.weeklyEntries = [];
state.historyCycles = [];
})
.finally(function () {
state.loading = false;
renderActivity();
});
}
function isMockMode() {
return new URLSearchParams(window.location.search).get('mock') === '1';
}
function formatRangeUTC(start, end) {
return formatDateTimeUTC(start) + '-' + formatDateTimeUTC(end) + '(UTC)';
}
function formatDateRange(start, end) {
return formatDateUTC(start).replace(/-/g, '/') + '-' + formatDateUTC(end).replace(/-/g, '/');
}
function formatDateTimeUTC(value) {
var date = new Date(Number(value || 0));
if (!Number(value || 0)) return '-';
return (
date.getUTCFullYear() +
'-' +
(date.getUTCMonth() + 1) +
'-' +
date.getUTCDate() +
' ' +
pad(date.getUTCHours()) +
':' +
pad(date.getUTCMinutes())
);
}
function formatDateUTC(value) {
var date = new Date(Number(value || 0));
if (!Number(value || 0)) return '-';
return date.getUTCFullYear() + '-' + pad(date.getUTCMonth() + 1) + '-' + pad(date.getUTCDate());
}
function formatNumber(value) {
return new Intl.NumberFormat('en-US').format(Number(value || 0));
}
function pad(value) {
return String(value).padStart(2, '0');
}
function escapeHTML(value) {
return String(value == null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function escapeAttr(value) {
return escapeHTML(value);
}
function init() {
tabs.forEach(function (tab) {
tab.addEventListener('click', function () {
var next = tab.getAttribute('data-tab-target');
setTab(next);
if (window.history && window.history.replaceState) {
window.history.replaceState(null, '', '#' + next);
}
});
});
window.addEventListener('resize', updateBodyHeight);
setTab(normalizeTab(window.location.hash.replace('#', '')));
if (isMockMode()) {
loadMock();
} else {
loadReal();
}
}
init();
})();