hyapp-h5/rank/index.html
2026-05-25 22:45:15 +08:00

482 lines
29 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
<title>Ranking</title>
<style>
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
:root { --app-scale: 1; }
html, body { margin: 0; width: 100%; min-height: 100%; overflow-x: hidden; background: #130a2b; font-family: Arial, Helvetica, sans-serif; color: #fff; }
button { border: 0; padding: 0; background: transparent; color: inherit; font: inherit; cursor: pointer; outline: none; }
.app-viewport { position: relative; width: 375px; min-height: 812px; margin: 0 auto; transform: scale(var(--app-scale)); transform-origin: top center; }
.page { position: relative; width: 375px; min-height: 812px; margin: 0; overflow-x: hidden; background: var(--page-bg, #7c2e33); }
.rank-page { padding-bottom: 126px; }
.rank-bg { position: absolute; left: -79px; top: var(--bg-top, -208px); width: 533px; height: 801px; object-fit: cover; filter: var(--bg-filter, none); pointer-events: none; }
.rank-bg.charm-layer { opacity: .72; filter: none; }
.top-shade { position: absolute; inset: 0 0 auto; height: 156px; pointer-events: none; background: linear-gradient(180deg, var(--shade, #3a1110), rgba(58,17,16,0)); }
.nav { position: relative; height: 88px; z-index: 2; }
.back { position: absolute; left: 16px; top: 10px; width: 24px; height: 24px; }
.back::before { content: ""; position: absolute; left: 8px; top: 5px; width: 12px; height: 12px; border-left: 2px solid #fff; border-bottom: 2px solid #fff; transform: rotate(45deg); }
.help { position: absolute; right: 18px; top: 11px; width: 18px; height: 18px; border: 1px solid rgba(255,255,255,.72); border-radius: 50%; color: rgba(255,255,255,.86); font-size: 13px; line-height: 17px; text-align: center; }
.tabs { position: absolute; left: 60px; right: 58px; top: 13px; display: grid; grid-template-columns: repeat(3, 1fr); align-items: start; }
.tab { position: relative; height: 32px; font-size: 16px; line-height: 16px; color: #e2e2e2; text-align: center; text-transform: capitalize; }
.tab.active { color: #ffc219; font-weight: 600; }
.tab.active::after { content: ""; position: absolute; left: 50%; bottom: 1px; width: 16px; height: 3px; margin-left: -8px; border-radius: 31px; background: #ffc219; }
.periods { position: absolute; left: 65px; top: 48px; width: 258px; height: 29px; border: 1px solid #ffc376; border-radius: 15px; background: #23180a; z-index: 2; }
.period { position: absolute; top: 0; width: 86px; height: 29px; color: #fff; font-size: 12px; line-height: 29px; text-align: center; }
.period[data-period="today"] { left: 0; }
.period[data-period="week"] { left: 86px; }
.period[data-period="month"] { left: 172px; }
.period.active { color: #492307; font-weight: 600; }
.period.active::before { content: ""; position: absolute; left: -6px; top: -5px; width: 92px; height: 36px; background: url("assets/period-active.png") center/100% 100% no-repeat; z-index: -1; }
.podium { position: relative; z-index: 1; height: 351px; margin-top: -44px; }
.podium-item { position: absolute; text-align: center; }
.podium-item .avatar { position: absolute; border-radius: 50%; object-fit: cover; }
.podium-frame { position: absolute; pointer-events: none; }
.podium-name { position: absolute; left: 50%; transform: translateX(-50%); width: 128px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 12px; line-height: 12px; text-transform: capitalize; }
.badges { position: absolute; display: flex; gap: 4px; justify-content: center; }
.badges img { width: 12px; height: 12px; }
.score-pill { position: absolute; left: 50%; transform: translateX(-50%); min-width: 116px; height: 18px; display: flex; align-items: center; justify-content: center; gap: 3px; font-size: 10px; line-height: 10px; background: linear-gradient(90deg, rgba(35,24,10,0), var(--score-bg-soft, rgba(35,24,10,.72)) 16%, var(--score-bg, #23180a) 33%, var(--score-bg, #23180a) 65%, var(--score-bg-soft, rgba(35,24,10,.72)) 80%, rgba(35,24,10,0)); }
.coin { width: 12px; height: 12px; object-fit: cover; flex: 0 0 auto; }
.top1 { left: 87px; top: 17px; width: 200px; height: 223px; }
.top1 .podium-frame { left: 0; top: 0; width: 200px; height: 200px; }
.top1 .avatar { left: 60px; top: 60px; width: 80px; height: 80px; }
.top1 .podium-name { top: 175px; }
.top1 .badges { left: 71px; top: 191px; }
.top1 .score-pill { top: 205px; }
.top2, .top3 { top: 198px; width: 154px; height: 181px; }
.top2 { left: 10px; }
.top3 { right: 10px; }
.top2 .podium-frame, .top3 .podium-frame { left: 0; top: 0; width: 154px; height: 154px; }
.top2 .avatar, .top3 .avatar { left: 45px; top: 45px; width: 64px; height: 64px; }
.top2 .podium-name, .top3 .podium-name { top: 137px; }
.top2 .badges, .top3 .badges { left: 47px; top: 153px; }
.top2 .score-pill, .top3 .score-pill { top: 167px; }
.list { position: relative; z-index: 1; padding: 44px 16px 16px; }
.rank-row { width: 100%; height: 52px; margin-bottom: 8px; display: grid; grid-template-columns: 26px 40px minmax(0, 1fr) 74px; align-items: center; gap: 0; border: 1px solid #ffc376; border-radius: 4px; background: var(--row-bg, rgba(35,24,10,.8)); text-align: left; }
.rank-no { font-size: 12px; text-align: center; }
.row-avatar { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; }
.row-main { min-width: 0; padding-left: 8px; text-align: left; }
.row-name { max-width: 172px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 12px; line-height: 14px; }
.row-badges { display: flex; gap: 4px; margin-top: 5px; }
.row-badges img { width: 12px; height: 12px; }
.row-score { display: flex; align-items: center; justify-content: flex-end; gap: 3px; padding-right: 18px; font-size: 12px; white-space: nowrap; }
.row-score .coin { width: 14px; height: 14px; }
.my-rank { position: absolute; left: 0; bottom: 0; z-index: 4; width: 375px; height: 80px; border: 1px solid #fcd98f; border-bottom: 0; border-radius: 10px 10px 0 0; background: linear-gradient(180deg, var(--mine-a, #bb1a1c), var(--mine-b, #601718)); box-shadow: inset 0 0 4px #000; overflow: hidden; }
.my-rank::before { content: ""; position: absolute; inset: 0; opacity: .08; background: radial-gradient(circle at 50% -20%, #fff 0, transparent 35%), repeating-radial-gradient(circle at 20px 20px, rgba(255,255,255,.5) 0 1px, transparent 2px 16px); pointer-events: none; }
.my-rank .rank-row { position: absolute; left: 16px; right: 16px; top: 12px; margin: 0; border-color: transparent; background: transparent; }
.home-indicator { position: absolute; left: 50%; bottom: 9px; z-index: 5; width: 134px; height: 5px; margin-left: -67px; border-radius: 100px; background: #fff; }
.empty { min-height: 180px; opacity: 0; }
.rank-state { position: relative; z-index: 1; min-height: 180px; display: flex; align-items: center; justify-content: center; padding: 24px 16px; color: rgba(255,255,255,.72); font-size: 12px; line-height: 16px; text-align: center; }
.loading-overlay { position: absolute; inset: 0; min-height: 812px; z-index: 20; display: none; align-items: center; justify-content: center; background: rgba(19,10,43,.32); backdrop-filter: blur(1px); pointer-events: auto; }
.loading-overlay.active { display: flex; }
.cat-loader { width: 72px; height: 72px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: rgba(0,0,0,.18); box-shadow: 0 8px 28px rgba(0,0,0,.28); }
.cat-loader svg { width: 54px; height: 54px; overflow: visible; animation: cat-float 1.1s ease-in-out infinite; }
.cat-loader .cat-line { fill: none; stroke: #fff; stroke-width: 3.4; stroke-linecap: round; stroke-linejoin: round; }
.cat-loader .cat-eye { transform-origin: center; animation: cat-blink 1.6s ease-in-out infinite; }
.cat-loader .cat-whisker { opacity: .9; animation: cat-whisker 1.1s ease-in-out infinite; }
@keyframes cat-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
@keyframes cat-blink { 0%, 72%, 100% { transform: scaleY(1); } 80%, 88% { transform: scaleY(.18); } }
@keyframes cat-whisker { 0%, 100% { opacity: .62; } 50% { opacity: 1; } }
.note-page { display: none; min-height: 812px; background: #130a2b; overflow: hidden; }
.note-page.active { display: block; }
.rank-page.hidden { display: none; }
.note-bg { position: absolute; left: -13px; top: -253px; width: 568px; height: 1065px; object-fit: cover; }
.note-nav { position: relative; height: 60px; z-index: 2; }
.note-title { position: absolute; top: 13px; left: 0; right: 0; font-size: 18px; line-height: 18px; text-align: center; font-weight: 600; }
.note-tabs { position: relative; z-index: 2; height: 45px; display: grid; grid-template-columns: repeat(3, 1fr); padding: 0 24px; }
.note-tab { position: relative; height: 32px; font-size: 18px; line-height: 18px; color: rgba(255,255,255,.5); text-transform: capitalize; text-align: left; }
.note-tab:nth-child(2) { text-align: center; }
.note-tab:nth-child(3) { text-align: right; }
.note-tab.active { color: #fff; }
.note-tab.active::after { content: ""; position: absolute; left: 50%; bottom: 0; width: 16px; height: 3px; margin-left: -8px; border-radius: 31px; background: #fff; }
.note-copy { position: relative; z-index: 2; margin: 2px 24px 0; width: 327px; min-height: 56px; font-size: 16px; line-height: 20px; }
.reward-panel { position: relative; z-index: 2; margin: 14px 24px 0; width: 327px; height: 474px; border: 2px solid #fff09b; background: #2a1351; box-shadow: inset 0 0 80px rgba(104,68,180,.7); }
.reward-panel::before, .reward-panel::after { content: ""; position: absolute; top: -12px; width: 163px; height: 27px; background: url("assets/note-border.png") center/100% 100% no-repeat; }
.reward-panel::before { left: 0; }
.reward-panel::after { right: 0; transform: scaleX(-1); }
.reward-title { margin-top: 26px; font-size: 20px; line-height: 20px; font-weight: 800; text-align: center; color: #fff; text-shadow: 0 2px 0 #be1e21; }
.reward-periods { position: relative; width: 295px; height: 36px; margin: 16px auto 0; border-radius: 8px; background: rgba(255,255,255,.08); display: grid; grid-template-columns: repeat(3, 1fr); }
.reward-period { height: 32px; margin: 2px; border-radius: 8px; font-size: 14px; line-height: 32px; text-align: center; }
.reward-period.active { background: #fff; color: #71179a; }
.reward-grid { width: 295px; margin: 16px auto 0; display: grid; grid-template-columns: repeat(2, 140px); gap: 8px 15px; }
.reward-card { height: 166px; border-radius: 8px; background: rgba(255,255,255,.08); text-align: center; overflow: hidden; }
.reward-card h3 { margin: 8px 0 8px; font-size: 16px; line-height: 16px; color: #fff; text-shadow: 0 2px 0 #be1e21; }
.reward-card img.reward { width: 80px; height: 80px; object-fit: contain; }
.reward-name { margin-top: 4px; font-size: 14px; line-height: 14px; }
.reward-value { margin-top: 6px; display: flex; justify-content: center; align-items: center; gap: 2px; font-size: 12px; line-height: 12px; }
</style>
</head>
<body>
<div id="appViewport" class="app-viewport">
<main id="rankPage" class="page rank-page">
<img id="rankBg" class="rank-bg" src="assets/wealth-bg.png" alt="">
<img id="rankBgCharm" class="rank-bg charm-layer" src="assets/charm-bg-b.png" alt="" hidden>
<div class="top-shade"></div>
<section class="nav">
<button class="back" id="backButton" aria-label="Back"></button>
<div class="tabs" id="boardTabs">
<button class="tab active" data-board="sent">Wealth</button>
<button class="tab" data-board="room">Room</button>
<button class="tab" data-board="received">Charm</button>
</div>
<button class="help" id="helpButton" aria-label="Note">?</button>
<div class="periods" id="periodTabs">
<button class="period active" data-period="today">Daily</button>
<button class="period" data-period="week">Weekly</button>
<button class="period" data-period="month">Monthly</button>
</div>
</section>
<section class="podium" id="podium"></section>
<section class="list" id="rankList"></section>
<section class="my-rank" id="myRank"></section>
<div class="home-indicator"></div>
</main>
<main id="notePage" class="page note-page">
<img class="note-bg" src="assets/note-bg.png" alt="">
<section class="note-nav">
<button class="back" id="noteBackButton" aria-label="Back"></button>
<div class="note-title">Note</div>
</section>
<section class="note-tabs" id="noteTabs">
<button class="note-tab active" data-board="sent">Wealth</button>
<button class="note-tab" data-board="room">Room</button>
<button class="note-tab" data-board="received">Charm</button>
</section>
<div class="note-copy" id="noteCopy"></div>
<section class="reward-panel">
<div class="reward-title">Ranking Rewards</div>
<div class="reward-periods" id="rewardPeriods">
<button class="reward-period active" data-period="today">Today</button>
<button class="reward-period" data-period="week">This Week</button>
<button class="reward-period" data-period="month">Monthly</button>
</div>
<div class="reward-grid" id="rewardGrid"></div>
</section>
<div class="home-indicator"></div>
</main>
<div id="loadingOverlay" class="loading-overlay" aria-hidden="true">
<div class="cat-loader" role="status" aria-label="Loading">
<svg viewBox="0 0 64 64" aria-hidden="true">
<path class="cat-line" d="M17 27 13 12 25 20" />
<path class="cat-line" d="M47 27 51 12 39 20" />
<path class="cat-line" d="M14 34c0-12 8-20 18-20s18 8 18 20c0 11-8 18-18 18S14 45 14 34Z" />
<path class="cat-line cat-eye" d="M25 33h.1" />
<path class="cat-line cat-eye" d="M39 33h.1" />
<path class="cat-line" d="M32 39v2" />
<path class="cat-line" d="M27 44c2 2 8 2 10 0" />
<path class="cat-line cat-whisker" d="M21 39h-8" />
<path class="cat-line cat-whisker" d="M22 43l-8 3" />
<path class="cat-line cat-whisker" d="M43 39h8" />
<path class="cat-line cat-whisker" d="M42 43l8 3" />
</svg>
</div>
</div>
</div>
<script src="../common/api.js"></script>
<script src="../common/params.js"></script>
<script src="../common/jsbridge.js"></script>
<script>
(function () {
var ASSETS = {
sent: { bg: "assets/wealth-bg.png", bg2: "", bgTop: "-208px", page: "#7c2e33", shade: "#3a1110", row: "rgba(35,24,10,.8)", score: "#23180a", mineA: "#bb1a1c", mineB: "#601718" },
room: { bg: "assets/wealth-bg.png", bg2: "", bgTop: "-208px", bgFilter: "hue-rotate(175deg) saturate(1.35) brightness(.7)", page: "#110f3d", shade: "#18103a", row: "rgba(9,13,35,.8)", score: "#090d23", mineA: "#1a4abb", mineB: "#040513" },
received: { bg: "assets/charm-bg-a.png", bg2: "assets/charm-bg-b.png", bgTop: "-208px", page: "#110f3d", shade: "#2f103a", row: "rgba(34,9,35,.8)", score: "#1a0923", mineA: "#551abb", mineB: "#1a0a2b" }
};
var state = { board: "sent", period: "today", page: 1, pageSize: 20, total: 0, items: [], myRank: null, loading: false, loaded: false, error: null };
var sampleNames = ["Namenamename", "Namenamename", "Namenamename", "Namenamename", "Namenamename", "Namenamename", "Namenamename", "Namenamename", "Namenamename"];
var noteCopy = {
sent: "The ranking is based on the total number of coins users have spent on gifts.",
room: "The ranking is based on the total number of coins received by rooms.",
received: "The ranking is based on the total number of coins users have received from gifts."
};
function byId(id) { return document.getElementById(id); }
function queryValue(name) {
return new URLSearchParams(window.location.search).get(name);
}
function mockEnabled() {
return queryValue("mock") === "1";
}
function money(value) {
var num = Number(value || 0);
if (num >= 1000000) return (num / 1000000).toFixed(num % 1000000 === 0 ? 0 : 1) + "m";
if (num >= 1000) return (num / 1000).toFixed(num % 1000 === 0 ? 0 : 1) + "k";
return String(num);
}
function userName(item) {
return (item.user && (item.user.username || item.user.display_user_id || item.user.user_id)) || (item.room && (item.room.title || item.room.room_short_id || item.room.room_id)) || item.room_id || item.user_id || "Namenamename";
}
function avatar(item, size) {
if (item.user && item.user.avatar) return item.user.avatar;
if (item.room && item.room.cover_url) return item.room.cover_url;
if (size === "large") return "assets/default-avatar-large.png";
if (size === "mid") return "assets/default-avatar-mid.png";
return "assets/default-avatar-small.png";
}
function sampleItems() {
return sampleNames.map(function (name, index) {
var rank = index + 1;
var base = rank <= 3 ? 123400 : 102400;
if (state.board === "room") {
return { rank: rank, room_id: "room_" + rank, gift_value: base, room: { room_id: "room_" + rank, title: name } };
}
return { rank: rank, user_id: String(10000 + rank), gift_value: base, user: { user_id: String(10000 + rank), username: name } };
});
}
function badges() {
var html = "";
for (var i = 0; i < 4; i += 1) html += '<img src="assets/vip.png" alt="">';
return html;
}
function score(value, className) {
return '<div class="' + className + '"><img class="coin" src="assets/coin.png" alt="">' + money(value) + "</div>";
}
function normalizeItem(raw) {
var item = raw || {};
var user = item.user || {};
var room = item.room || {};
var userId = item.user_id || user.user_id || "";
var roomId = item.room_id || room.room_id || "";
return {
rank: Number(item.rank || 0),
user_id: String(userId || ""),
room_id: String(roomId || ""),
gift_value: Number(item.gift_value || item.giftValue || item.value || 0),
gift_count: Number(item.gift_count || item.giftCount || 0),
transaction_count: Number(item.transaction_count || item.transactionCount || 0),
last_gift_at_ms: Number(item.last_gift_at_ms || item.lastGiftAtMs || 0),
user: userId ? {
user_id: String(userId),
display_user_id: user.display_user_id || user.displayUserId || "",
username: user.username || user.nickname || user.name || "",
avatar: user.avatar || user.avatar_url || user.avatarUrl || ""
} : null,
room: roomId ? {
room_id: String(roomId),
room_short_id: room.room_short_id || room.roomShortId || "",
title: room.title || room.name || "",
cover_url: room.cover_url || room.coverUrl || room.room_avatar || room.roomAvatar || room.avatar || ""
} : null
};
}
function normalizePayload(data, reset) {
var items = (data && Array.isArray(data.items) ? data.items : []).map(normalizeItem).filter(function (item) { return item.rank > 0; });
state.total = Number((data && data.total) || items.length || 0);
state.page = Number((data && data.page) || state.page);
state.pageSize = Number((data && (data.page_size || data.pageSize)) || state.pageSize);
state.myRank = data && data.my_rank ? normalizeItem(data.my_rank) : null;
state.items = reset ? items : state.items.concat(items);
state.loaded = true;
state.error = null;
}
function visibleItems() {
if (state.items.length) return state.items;
return mockEnabled() ? sampleItems() : [];
}
function renderPodium() {
var items = visibleItems();
var top = [items[0], items[1], items[2]];
var html = "";
if (top[0]) {
html += '<button class="podium-item top1" data-index="0"><img class="avatar" src="' + avatar(top[0], "large") + '" alt=""><img class="podium-frame" src="assets/top-frame.png" alt=""><div class="podium-name">' + userName(top[0]) + '</div><div class="badges">' + badges() + '</div>' + score(top[0].gift_value, "score-pill") + "</button>";
}
if (top[1]) {
html += '<button class="podium-item top2" data-index="1"><img class="avatar" src="' + avatar(top[1], "mid") + '" alt=""><img class="podium-frame" src="assets/side-frame-blue.png" alt=""><div class="podium-name">' + userName(top[1]) + '</div><div class="badges">' + badges() + '</div>' + score(top[1].gift_value, "score-pill") + "</button>";
}
if (top[2]) {
html += '<button class="podium-item top3" data-index="2"><img class="avatar" src="' + avatar(top[2], "mid") + '" alt=""><img class="podium-frame" src="assets/side-frame-red.png" alt=""><div class="podium-name">' + userName(top[2]) + '</div><div class="badges">' + badges() + '</div>' + score(top[2].gift_value, "score-pill") + "</button>";
}
byId("podium").innerHTML = html;
}
function row(item, mine) {
if (!item) return "";
return '<button class="rank-row" data-rank="' + item.rank + '"><div class="rank-no">' + item.rank + '</div><img class="row-avatar" src="' + avatar(item, "small") + '" alt=""><div class="row-main"><div class="row-name">' + userName(item) + '</div><div class="row-badges">' + badges() + '</div></div>' + score(item.gift_value, "row-score") + "</button>";
}
function renderList() {
var items = visibleItems();
if (!items.length) {
byId("rankList").innerHTML = '<div class="rank-state">' + (state.error ? "Network error" : ((state.loading || !state.loaded) ? "Loading" : "No ranking data")) + '</div>';
byId("myRank").innerHTML = "";
return;
}
byId("rankList").innerHTML = items.slice(3).map(function (item) { return row(item); }).join("") + '<div class="empty"></div>';
byId("myRank").innerHTML = row(state.myRank || items[3] || items[0]);
}
function applyTheme() {
var cfg = ASSETS[state.board];
document.documentElement.style.setProperty("--page-bg", cfg.page);
document.documentElement.style.setProperty("--shade", cfg.shade);
document.documentElement.style.setProperty("--row-bg", cfg.row);
document.documentElement.style.setProperty("--score-bg", cfg.score);
document.documentElement.style.setProperty("--score-bg-soft", cfg.score.replace(")", ", .72)").replace("rgb", "rgba"));
document.documentElement.style.setProperty("--mine-a", cfg.mineA);
document.documentElement.style.setProperty("--mine-b", cfg.mineB);
document.documentElement.style.setProperty("--bg-top", cfg.bgTop);
document.documentElement.style.setProperty("--bg-filter", cfg.bgFilter || "none");
byId("rankBg").src = cfg.bg;
byId("rankBgCharm").hidden = !cfg.bg2;
if (cfg.bg2) byId("rankBgCharm").src = cfg.bg2;
}
function syncTabs() {
document.querySelectorAll("[data-board]").forEach(function (node) { node.classList.toggle("active", node.dataset.board === state.board); });
document.querySelectorAll("[data-period]").forEach(function (node) { node.classList.toggle("active", node.dataset.period === state.period); });
}
function activePage() {
return byId("notePage").classList.contains("active") ? byId("notePage") : byId("rankPage");
}
function resizeApp() {
var designWidth = 375;
var scale = Math.min(window.innerWidth / designWidth, 1.25);
var page = activePage();
var height = Math.max(812, page.scrollHeight);
document.documentElement.style.setProperty("--app-scale", String(scale));
byId("appViewport").style.height = height + "px";
document.body.style.minHeight = Math.ceil(height * scale) + "px";
}
function scheduleResize() {
window.requestAnimationFrame(resizeApp);
}
function syncLoading() {
var overlay = byId("loadingOverlay");
overlay.classList.toggle("active", state.loading);
overlay.setAttribute("aria-hidden", state.loading ? "false" : "true");
}
function render() {
applyTheme();
syncTabs();
syncLoading();
renderPodium();
renderList();
scheduleResize();
}
function load(reset) {
if (state.loading) return Promise.resolve();
if (reset) {
state.page = 1;
state.items = [];
state.total = 0;
state.myRank = null;
state.loaded = false;
state.error = null;
render();
}
if (mockEnabled()) {
state.loaded = true;
state.error = null;
return Promise.resolve(render());
}
if (!window.HyAppAPI || !window.HyAppAPI.getAccessToken()) {
state.loaded = true;
state.error = new Error("missing_token");
return Promise.resolve(render());
}
state.loading = true;
state.error = null;
render();
return window.HyAppAPI.get("/api/v1/activities/user-leaderboards", {
board_type: state.board,
period: state.period,
page: state.page,
page_size: state.pageSize
}).then(function (data) {
normalizePayload(data, reset);
render();
}).catch(function (error) {
state.loaded = true;
state.error = error;
render();
}).finally(function () {
state.loading = false;
render();
});
}
function openItem(item) {
if (!item) return;
if (state.board === "room") {
window.HyAppBridge.openRoom(item.room_id || (item.room && item.room.room_id));
} else {
window.HyAppBridge.openUser(item.user_id || (item.user && item.user.user_id));
}
}
function showNote() {
byId("rankPage").classList.add("hidden");
byId("notePage").classList.add("active");
byId("noteCopy").textContent = noteCopy[state.board];
renderRewards();
scheduleResize();
window.HyAppBridge.openRankNote(state.board);
}
function hideNote() {
byId("notePage").classList.remove("active");
byId("rankPage").classList.remove("hidden");
scheduleResize();
}
function renderRewards() {
byId("noteCopy").textContent = noteCopy[state.board];
byId("rewardGrid").innerHTML = [1, 2, 3, 4].map(function () {
return '<div class="reward-card"><h3>Top1</h3><img class="reward" src="assets/reward.png" alt=""><div class="reward-name">Namename</div><div class="reward-value"><img class="coin" src="assets/coin.png" alt="">10000/7days</div></div>';
}).join("");
scheduleResize();
}
byId("boardTabs").addEventListener("click", function (event) {
var target = event.target.closest("[data-board]");
if (!target || target.dataset.board === state.board) return;
state.board = target.dataset.board;
load(true);
});
byId("noteTabs").addEventListener("click", function (event) {
var target = event.target.closest("[data-board]");
if (!target) return;
state.board = target.dataset.board;
syncTabs();
renderRewards();
});
byId("periodTabs").addEventListener("click", function (event) {
var target = event.target.closest("[data-period]");
if (!target || target.dataset.period === state.period) return;
state.period = target.dataset.period;
load(true);
});
byId("rewardPeriods").addEventListener("click", function (event) {
var target = event.target.closest("[data-period]");
if (!target) return;
state.period = target.dataset.period;
syncTabs();
});
byId("podium").addEventListener("click", function (event) {
var target = event.target.closest("[data-index]");
if (target) openItem(visibleItems()[Number(target.dataset.index)]);
});
byId("rankList").addEventListener("click", function (event) {
var target = event.target.closest("[data-rank]");
if (!target) return;
var item = visibleItems().find(function (candidate) { return String(candidate.rank) === target.dataset.rank; });
openItem(item);
});
byId("myRank").addEventListener("click", function () { openItem(state.myRank); });
byId("backButton").addEventListener("click", function () { window.HyAppBridge.back(); });
byId("helpButton").addEventListener("click", showNote);
byId("noteBackButton").addEventListener("click", hideNote);
window.addEventListener("scroll", function () {
if (!state.total || state.items.length >= state.total || state.loading) return;
if (window.innerHeight + window.scrollY > document.body.scrollHeight - 180) {
state.page += 1;
load(false);
}
});
window.addEventListener("resize", resizeApp);
window.addEventListener("orientationchange", scheduleResize);
render();
window.HyAppBridge.ready({ page: "rank" });
load(true).finally(function () {
window.HyAppParams.loadUser().catch(function () { return null; });
});
})();
</script>
</body>
</html>