h5活动相关

This commit is contained in:
zhx 2026-06-06 13:42:01 +08:00
parent f2d0ae3ee9
commit a006d26a50
131 changed files with 4372 additions and 2 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

@ -0,0 +1,12 @@
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 534 93" fill="none" xmlns="http://www.w3.org/2000/svg">
<foreignObject x="-8" y="-8" width="550" height="109"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(4px);clip-path:url(#bgblur_0_0_5_clip_path);height:100%;width:100%"></div></foreignObject><path id="Rectangle 52" data-figma-bg-blur-radius="8" d="M511 2C522.598 2.00001 532 11.402 532 23V70C532 81.598 522.598 91 511 91H23C11.402 91 2 81.598 2 70V23C2 11.402 11.402 2 23 2H511Z" fill="var(--fill-0, #101A37)" stroke="url(#paint0_linear_0_5)" stroke-width="4"/>
<defs>
<clipPath id="bgblur_0_0_5_clip_path" transform="translate(8 8)"><path d="M511 2C522.598 2.00001 532 11.402 532 23V70C532 81.598 522.598 91 511 91H23C11.402 91 2 81.598 2 70V23C2 11.402 11.402 2 23 2H511Z"/>
</clipPath><linearGradient id="paint0_linear_0_5" x1="4" y1="4" x2="9.56551" y2="115.076" gradientUnits="userSpaceOnUse">
<stop stop-color="#E8A96B"/>
<stop offset="0.288462" stop-color="#FDE3B8"/>
<stop offset="0.6875" stop-color="#DF944B"/>
<stop offset="1" stop-color="#FDE3B8"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

View File

@ -0,0 +1,3 @@
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 814 139" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="Rectangle 52" d="M0 19C0 8.50659 8.50659 0 19 0H795C805.493 0 814 8.50659 814 19V120C814 130.493 805.493 139 795 139H19C8.5066 139 0 130.493 0 120V19Z" fill="var(--fill-0, #0648B4)" fill-opacity="0.26"/>
</svg>

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 KiB

View File

@ -0,0 +1,3 @@
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 71 71" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="Union" d="M38.8516 32.1465H71V38.8506H38.8516V71H32.1484V38.8506H0V32.1465H32.1484V0H38.8516V32.1465Z" fill="var(--fill-0, #F6E0B2)"/>
</svg>

After

Width:  |  Height:  |  Size: 322 B

View File

@ -0,0 +1,3 @@
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 108 108" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect id="Rectangle 46" width="108" height="108" rx="54" fill="var(--fill-0, #D9D9D9)"/>
</svg>

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -0,0 +1,3 @@
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 880 149" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="Rectangle 52" d="M0 19C0 8.50659 8.50659 0 19 0H861C871.493 0 880 8.50659 880 19V130C880 140.493 871.493 149 861 149H19C8.50657 149 0 140.493 0 130V19Z" fill="var(--fill-0, #0648B4)" fill-opacity="0.26"/>
</svg>

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -0,0 +1,3 @@
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 880 169" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="Rectangle 52" d="M0 19C0 8.50659 8.50659 0 19 0H861C871.493 0 880 8.50659 880 19V150C880 160.493 871.493 169 861 169H19C8.50657 169 0 160.493 0 150V19Z" fill="var(--fill-0, #0648B4)" fill-opacity="0.26"/>
</svg>

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

351
activity/invite/index.html Normal file
View File

@ -0,0 +1,351 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<title>Invite Friends</title>
<link rel="stylesheet" href="./style.css?v=20260606-9" />
</head>
<body>
<main
class="invite-page"
aria-label="Invite friends"
data-i18n-aria="invite.pageAria"
>
<div class="language-switcher">
<button
class="language-button"
type="button"
aria-label="Change language"
aria-expanded="false"
data-language-toggle
data-current-lang
data-i18n-aria="invite.changeLanguage"
>
EN
</button>
<div class="language-menu" data-language-menu hidden>
<button type="button" data-lang-option="en">EN</button>
<button type="button" data-lang-option="ar">AR</button>
<button type="button" data-lang-option="tr">TR</button>
<button type="button" data-lang-option="es">ES</button>
<button type="button" data-lang-option="zh">ZH</button>
<button type="button" data-lang-option="id">ID</button>
</div>
</div>
<section
class="hero"
aria-label="Invite friends make money"
data-i18n-aria="invite.heroAria"
>
<div
class="countdown-card"
aria-label="Countdown"
data-i18n-aria="invite.countdownAria"
>
<div class="time-cell">
<strong id="days">01</strong>
<span data-i18n="invite.days">Days</span>
</div>
<div class="time-cell">
<strong id="hours">01</strong>
<span data-i18n="invite.hours">Hours</span>
</div>
<div class="time-cell">
<strong id="minutes">01</strong>
<span data-i18n="invite.minutes">Minutes</span>
</div>
<div class="time-cell">
<strong id="seconds">01</strong>
<span data-i18n="invite.seconds">Seconds</span>
</div>
</div>
</section>
<section class="ornate-panel reward-panel">
<h2 data-i18n="invite.yourReward">Your Reward</h2>
<p class="subtle" data-i18n="invite.rewardSubtitle">
(Earn for every friend you invite)
</p>
<div class="reward-grid">
<article class="reward-item">
<img
class="reward-orb"
src="./assets/reward-orb-bg.png"
alt=""
/>
<img
class="reward-gold"
src="./assets/gold-stack.png"
alt=""
/>
<strong>+2000</strong>
<span data-i18n="invite.invitationReceived">
Invitation received
</span>
</article>
<img class="plus" src="./assets/plus.svg" alt="+" />
<article class="reward-item">
<img
class="reward-orb"
src="./assets/reward-orb-bg.png"
alt=""
/>
<img
class="reward-gold"
src="./assets/gold-stack.png"
alt=""
/>
<strong>+1000</strong>
<span data-i18n="invite.topUpViaFriends">
Top up via friends
</span>
</article>
</div>
</section>
<section class="invite-copy">
<h2 data-i18n="invite.startInviting">
Start inviting people now
</h2>
<p class="subtle" data-i18n="invite.copyInviteHint">
(Copy your invitation link and share it with your friends)
</p>
<div class="invite-fields">
<div class="link-box" id="inviteLink">
http://yourappbalbalanal
</div>
<div class="link-box invite-code" id="inviteCode">
ABC123
</div>
</div>
<div class="invite-actions">
<button
class="ornate-button"
id="copyLinkButton"
type="button"
data-i18n="invite.copy"
>
Copy
</button>
<button
class="ornate-button small-copy-button"
id="copyCodeButton"
type="button"
data-i18n="invite.copy"
>
Copy
</button>
</div>
</section>
<section class="ornate-panel stats-panel" aria-label="Invite stats">
<div class="stat">
<strong>168</strong>
<span data-i18n="invite.totalInvitees">
Total number of invitees
</span>
</div>
<div class="stat">
<strong>01</strong>
<span data-i18n="invite.eligibleVoters">
Number of eligible voters
</span>
</div>
<div class="stat">
<strong>01</strong>
<span data-i18n="invite.totalRecharged">
Total amount recharged by invitees
</span>
</div>
<div class="stat">
<strong>12354</strong>
<span data-i18n="invite.myRewards">My Rewards</span>
</div>
</section>
<section class="ornate-panel monthly-panel" id="monthlyTask">
<h2 data-i18n="invite.monthlyTasks">Monthly Tasks</h2>
<div
class="task-tabs"
role="tablist"
aria-label="Monthly tasks"
data-i18n-aria="invite.monthlyTasks"
>
<button
class="task-tab is-active"
id="transportTab"
type="button"
role="tab"
aria-selected="true"
data-tab="transport"
>
<span data-i18n="invite.totalTransportation">
Total Transportation
</span>
<span data-i18n="invite.volume">Volume</span>
</button>
<button
class="task-tab"
id="quotaTab"
type="button"
role="tab"
aria-selected="false"
data-tab="quota"
>
<span data-i18n="invite.invitationQuota">
Invitation Quota
</span>
<span data-i18n="invite.task">Task</span>
</button>
</div>
<div class="task-pane is-active" id="transportPane">
<div class="timeline" aria-label="Transportation milestones">
<article class="milestone is-done">
<h3 data-i18n="invite.milestoneTitle">
Milestone1:balabalabal
</h3>
<p data-i18n="invite.totalSupply">
Total supply: 49999 coins
</p>
</article>
<article class="milestone is-current">
<h3 data-i18n="invite.milestoneTitle">
Milestone1:balabalabal
</h3>
<p data-i18n="invite.totalSupply">
Total supply: 49999 coins
</p>
</article>
<article class="milestone">
<h3 data-i18n="invite.milestoneTitle">
Milestone1:balabalabal
</h3>
<p data-i18n="invite.totalSupply">
Total supply: 49999 coins
</p>
</article>
<article class="milestone">
<h3 data-i18n="invite.milestoneTitle">
Milestone1:balabalabal
</h3>
<p data-i18n="invite.totalSupply">
Total supply: 49999 coins
</p>
</article>
<article class="milestone">
<h3 data-i18n="invite.milestoneTitle">
Milestone1:balabalabal
</h3>
<p data-i18n="invite.totalSupply">
Total supply: 49999 coins
</p>
</article>
</div>
<button
class="ornate-button copy-task-button"
type="button"
data-i18n="invite.copy"
>
Copy
</button>
<p class="subtle footnote" data-i18n="invite.transportFootnote">
(Only the highest-level bonuses may be awarded)
</p>
</div>
<div class="task-pane" id="quotaPane">
<div class="quota-list" aria-label="Invitation quota tasks">
<article class="quota-card">
<div>
<h3 data-i18n="invite.milestoneTitle">
Milestone1:balabalabal
</h3>
<p data-i18n="invite.invitePaidFriends">
Invite 10 friends who have already made a
payment
</p>
</div>
<button
class="claim-button is-ready"
type="button"
data-i18n="invite.claim"
>
Claim
</button>
</article>
<article class="quota-card">
<div>
<h3 data-i18n="invite.milestoneTitle">
Milestone1:balabalabal
</h3>
<p data-i18n="invite.invitePaidFriends">
Invite 10 friends who have already made a
payment
</p>
</div>
<button
class="claim-button"
type="button"
data-i18n="invite.claim"
>
Claim
</button>
</article>
<article class="quota-card">
<div>
<h3 data-i18n="invite.milestoneTitle">
Milestone1:balabalabal
</h3>
<p data-i18n="invite.invitePaidFriends">
Invite 10 friends who have already made a
payment
</p>
</div>
<button
class="claim-button"
type="button"
data-i18n="invite.claim"
>
Claim
</button>
</article>
</div>
<p class="subtle footnote" data-i18n="invite.quotaFootnote">
(You'll receive a reward upon reaching each level)
</p>
</div>
</section>
<section class="ornate-panel rank-panel" id="rankPanel" hidden>
<h2 data-i18n="invite.rank">Rank</h2>
<div class="rank-list" id="rankList"></div>
</section>
</main>
<script>
(function () {
try {
var params = new URLSearchParams(window.location.search);
if (
!params.get("lang") &&
!window.localStorage.getItem("invite_lang_ready")
) {
window.localStorage.setItem("hyapp_h5_lang", "en");
window.localStorage.setItem("invite_lang_ready", "1");
}
} catch (error) {
document.documentElement.lang = "en";
}
})();
</script>
<script src="../../common/i18n.js?v=20260606-1"></script>
<script src="../../common/toast.js"></script>
<script src="./script.js?v=20260606-6"></script>
</body>
</html>

206
activity/invite/script.js Normal file
View File

@ -0,0 +1,206 @@
(function () {
var rankRows = Array.from({ length: 30 }, function (_, index) {
return {
rank: index + 1,
name: "hanneem",
score: "123",
};
});
function $(selector) {
return document.querySelector(selector);
}
function toast(message) {
if (window.HyToast && window.HyToast.show) {
window.HyToast.show(message);
return;
}
window.alert(message);
}
function t(key, fallback) {
if (window.HyAppI18n && window.HyAppI18n.t) {
return window.HyAppI18n.t(key, fallback);
}
return fallback || key;
}
function formatPeople(value) {
return t("invite.peopleSuffix", "{value} people").replace(
"{value}",
value
);
}
function pad(value) {
return String(Math.max(0, value)).padStart(2, "0");
}
function updateCountdown() {
var now = Date.now();
var end = new Date();
end.setDate(end.getDate() + 1);
end.setHours(0, 0, 0, 0);
var diff = Math.max(0, end.getTime() - 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;
$("#days").textContent = pad(days);
$("#hours").textContent = pad(hours);
$("#minutes").textContent = pad(minutes);
$("#seconds").textContent = pad(seconds);
}
function renderRank() {
var list = $("#rankList");
list.textContent = "";
rankRows.forEach(function (item) {
var row = document.createElement("article");
var rank = document.createElement("span");
var medal = document.createElement("img");
var avatar = document.createElement("img");
var name = document.createElement("strong");
var score = document.createElement("span");
row.className = "rank-row";
rank.className = "rank-no";
medal.className = "rank-medal";
avatar.className = "rank-avatar";
name.className = "rank-name";
score.className = "rank-score";
if (item.rank <= 3) {
medal.src = "./assets/medal-" + item.rank + ".png";
medal.alt = "Rank " + item.rank;
rank.textContent = "";
} else {
medal.alt = "";
rank.textContent = item.rank;
}
avatar.src = "./assets/rank-avatar.png";
avatar.alt = "";
name.textContent = item.name;
score.textContent = formatPeople(item.score);
row.appendChild(rank);
row.appendChild(item.rank <= 3 ? medal : document.createElement("span"));
row.appendChild(avatar);
row.appendChild(name);
row.appendChild(score);
list.appendChild(row);
});
}
function setTab(tabName) {
var monthly = $("#monthlyTask");
var isQuota = tabName === "quota";
monthly.classList.toggle("is-quota", isQuota);
document.querySelectorAll(".task-tab").forEach(function (button) {
var active = button.getAttribute("data-tab") === tabName;
button.classList.toggle("is-active", active);
button.setAttribute("aria-selected", String(active));
});
$("#transportPane").classList.toggle("is-active", !isQuota);
$("#quotaPane").classList.toggle("is-active", isQuota);
$("#rankPanel").hidden = !isQuota;
}
function copyTextFrom(selector) {
var node = $(selector);
var text = node ? node.textContent.trim() : "";
if (!text) {
toast(t("invite.copyFailed", "Copy failed"));
return;
}
function fallbackCopy() {
var input = document.createElement("textarea");
input.value = text;
input.setAttribute("readonly", "readonly");
input.style.position = "fixed";
input.style.left = "-9999px";
input.style.top = "0";
document.body.appendChild(input);
input.select();
var ok = false;
try {
ok = document.execCommand("copy");
} catch (error) {
ok = false;
}
document.body.removeChild(input);
toast(
ok
? t("invite.copied", "Copied")
: t("invite.copyFailed", "Copy failed")
);
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard
.writeText(text)
.then(function () {
toast(t("invite.copied", "Copied"));
})
.catch(function () {
fallbackCopy();
});
return;
}
fallbackCopy();
}
function copyInviteLink() {
copyTextFrom("#inviteLink");
}
function copyInviteCode() {
copyTextFrom("#inviteCode");
}
function bindEvents() {
document.querySelectorAll(".task-tab").forEach(function (button) {
button.addEventListener("click", function () {
setTab(button.getAttribute("data-tab") || "transport");
});
});
$("#copyLinkButton").addEventListener("click", copyInviteLink);
$("#copyCodeButton").addEventListener("click", copyInviteCode);
document
.querySelectorAll(".copy-task-button")
.forEach(function (button) {
button.addEventListener("click", copyInviteLink);
});
document.querySelectorAll(".claim-button").forEach(function (button) {
button.addEventListener("click", function () {
if (!button.classList.contains("is-ready")) {
toast(t("invite.notAvailable", "Not available yet"));
return;
}
toast(t("invite.claimed", "Claimed"));
button.classList.remove("is-ready");
});
});
}
function init() {
renderRank();
updateCountdown();
setInterval(updateCountdown, 1000);
bindEvents();
}
window.addEventListener("hyapp:i18n-ready", function () {
renderRank();
});
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();

657
activity/invite/style.css Normal file
View File

@ -0,0 +1,657 @@
:root {
--page-width: min(100vw, 430px);
--u: calc(var(--page-width) / 1080);
--blue: #071954;
--gold: #f6e0b2;
--white: #fff;
}
* {
box-sizing: border-box;
}
html,
body {
min-height: 100%;
margin: 0;
background: #030b24;
color: var(--white);
font-family:
"Source Han Sans SC", "Noto Sans", "Helvetica Neue", Arial, sans-serif;
}
button {
border: 0;
padding: 0;
color: inherit;
font: inherit;
background: transparent;
-webkit-tap-highlight-color: transparent;
}
.invite-page {
position: relative;
width: var(--page-width);
min-height: 100vh;
margin: 0 auto;
overflow: hidden;
background: var(--blue);
}
.language-switcher {
position: absolute;
z-index: 20;
top: calc(34 * var(--u));
right: calc(34 * var(--u));
width: calc(190 * var(--u));
direction: ltr;
}
.language-button {
display: flex;
align-items: center;
justify-content: center;
width: calc(156 * var(--u));
height: calc(82 * var(--u));
margin-left: auto;
border: calc(2 * var(--u)) solid rgba(255, 232, 184, 0.62);
border-radius: 999px;
color: #ffe8b8;
font-size: calc(34 * var(--u));
font-weight: 800;
line-height: 1;
letter-spacing: 0;
cursor: pointer;
background: rgba(25, 9, 1, 0.58);
box-shadow:
inset 0 0 calc(18 * var(--u)) rgba(255, 210, 93, 0.18),
0 calc(8 * var(--u)) calc(18 * var(--u)) rgba(0, 0, 0, 0.28);
}
.language-menu {
position: absolute;
top: calc(94 * var(--u));
right: 0;
width: calc(190 * var(--u));
overflow: hidden;
border: calc(2 * var(--u)) solid rgba(255, 232, 184, 0.56);
border-radius: calc(18 * var(--u));
background: rgba(25, 9, 1, 0.92);
box-shadow: 0 calc(16 * var(--u)) calc(30 * var(--u))
rgba(0, 0, 0, 0.34);
}
.language-menu button {
display: block;
width: 100%;
height: calc(58 * var(--u));
color: #fff5dc;
font-size: calc(28 * var(--u));
font-weight: 700;
line-height: 1;
cursor: pointer;
}
.language-menu button.is-active {
color: #ffd25d;
background: rgba(255, 210, 93, 0.22);
}
.hero {
position: relative;
height: calc(1110 * var(--u));
background-image: url("./assets/hero-bg.png");
background-repeat: no-repeat;
background-position: top center;
background-size: 100% auto;
}
.countdown-card {
position: absolute;
left: calc(32 * var(--u));
bottom: calc(0 * var(--u));
width: calc(1016 * var(--u));
height: calc(290 * var(--u));
background: url("./assets/countdown-panel.png") center / 100% 100%
no-repeat;
}
.time-cell {
position: absolute;
top: 0;
left: calc(var(--countdown-center) * var(--u));
width: calc(180 * var(--u));
height: 100%;
transform: translateX(-50%);
color: var(--gold);
text-align: center;
}
.time-cell:nth-child(1) {
--countdown-center: 172;
}
.time-cell:nth-child(2) {
--countdown-center: 391;
}
.time-cell:nth-child(3) {
--countdown-center: 614;
}
.time-cell:nth-child(4) {
--countdown-center: 841;
}
.time-cell strong {
position: absolute;
top: calc(107 * var(--u));
left: 0;
display: flex;
width: 100%;
height: calc(46 * var(--u));
align-items: center;
justify-content: center;
font-size: calc(56 * var(--u));
line-height: 1;
font-weight: 700;
}
.time-cell span {
position: absolute;
top: calc(216 * var(--u));
left: 50%;
margin: 0;
transform: translateX(-50%);
font-size: calc(32 * var(--u));
line-height: 1.1;
white-space: nowrap;
}
.ornate-panel {
position: relative;
width: calc(1016 * var(--u));
margin: 0 auto;
overflow: hidden;
background-image:
url("./assets/panel-top-a.png"), url("./assets/panel-bottom-a.png"),
url("./assets/panel-middle-a.png");
background-repeat: no-repeat, no-repeat, no-repeat;
background-position: top center, bottom center, center center;
background-size: 100% auto, 100% auto, 100% 100%;
}
.ornate-panel > * {
position: relative;
z-index: 1;
}
h2,
h3,
p {
margin: 0;
}
.ornate-panel h2,
.invite-copy h2,
.rank-panel h2 {
color: var(--gold);
text-align: center;
font-weight: 800;
text-shadow: 0 calc(2 * var(--u)) calc(4 * var(--u)) rgba(0, 0, 0, 0.35);
}
.subtle {
color: rgba(255, 255, 255, 0.9);
text-align: center;
}
.reward-panel {
height: calc(790 * var(--u));
margin-top: calc(20 * var(--u));
padding-top: calc(122 * var(--u));
background-image: url("./assets/reward-panel-bg.png");
background-position: center;
background-size: 100% 100%;
}
.reward-panel h2 {
font-size: calc(60 * var(--u));
line-height: 1;
}
.reward-panel .subtle {
margin-top: calc(22 * var(--u));
font-size: calc(30 * var(--u));
}
.reward-grid {
position: relative;
width: calc(1016 * var(--u));
height: calc(486 * var(--u));
margin: calc(28 * var(--u)) auto 0;
}
.reward-item {
position: absolute;
top: 0;
width: calc(486 * var(--u));
height: calc(486 * var(--u));
color: var(--gold);
text-align: center;
}
.reward-item:first-child {
left: calc(8 * var(--u));
}
.reward-item:last-child {
left: calc(522 * var(--u));
}
.reward-orb {
position: absolute;
z-index: 0;
display: block;
inset: 0;
width: calc(486 * var(--u));
height: calc(486 * var(--u));
}
.reward-gold {
position: absolute;
z-index: 1;
top: 50%;
left: 50%;
width: calc(178 * var(--u));
transform: translate(-50%, -50%);
}
.reward-item strong {
position: absolute;
top: calc(98 * var(--u));
left: calc(290 * var(--u));
font-size: calc(40 * var(--u));
line-height: 1;
white-space: nowrap;
}
.reward-item span {
position: absolute;
top: calc(421 * var(--u));
left: 50%;
display: block;
width: calc(360 * var(--u));
transform: translateX(-50%);
color: var(--gold);
font-size: calc(30 * var(--u));
line-height: 1.2;
}
.plus {
position: absolute;
top: calc(207 * var(--u));
left: calc(472 * var(--u));
display: block;
width: calc(71 * var(--u));
height: calc(71 * var(--u));
}
.invite-copy {
width: calc(1016 * var(--u));
margin: calc(42 * var(--u)) auto 0;
text-align: center;
}
.invite-copy h2 {
font-size: calc(46 * var(--u));
line-height: 1;
}
.invite-copy .subtle {
margin-top: calc(28 * var(--u));
font-size: calc(30 * var(--u));
}
.invite-fields {
display: grid;
grid-template-columns: minmax(0, 1fr) calc(286 * var(--u));
gap: calc(24 * var(--u));
width: calc(760 * var(--u));
margin: calc(58 * var(--u)) auto 0;
}
.link-box {
display: flex;
align-items: center;
justify-content: center;
min-width: 0;
height: calc(85 * var(--u));
padding: 0 calc(28 * var(--u));
overflow: hidden;
color: var(--gold);
font-size: calc(30 * var(--u));
text-overflow: ellipsis;
white-space: nowrap;
background: url("./assets/link-input.svg") center / 100% 100% no-repeat;
}
.invite-code {
font-weight: 700;
letter-spacing: calc(1 * var(--u));
}
.invite-actions {
display: flex;
width: calc(764 * var(--u));
align-items: center;
justify-content: center;
gap: calc(24 * var(--u));
margin: calc(50 * var(--u)) auto 0;
}
.ornate-button {
display: flex;
align-items: center;
justify-content: center;
width: calc(774 * var(--u));
height: calc(133 * var(--u));
margin: 0 auto;
color: var(--gold);
font-size: calc(50 * var(--u));
font-weight: 800;
line-height: 1;
text-shadow: 0 calc(2 * var(--u)) calc(4 * var(--u)) rgba(0, 0, 0, 0.35);
background: url("./assets/button-active-wide.png") center / 100% 100%
no-repeat;
}
.ornate-button:active {
transform: translateY(calc(2 * var(--u)));
}
.invite-actions .ornate-button {
flex: 0 0 calc(520 * var(--u));
width: calc(520 * var(--u));
height: calc(100 * var(--u));
font-size: calc(42 * var(--u));
}
.invite-actions .small-copy-button {
flex: 0 0 calc(220 * var(--u));
width: calc(220 * var(--u));
height: calc(78 * var(--u));
font-size: calc(30 * var(--u));
background-image: url("./assets/claim-active.png");
}
.stats-panel {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: calc(16 * var(--u)) calc(30 * var(--u));
height: calc(440 * var(--u));
margin-top: calc(62 * var(--u));
padding: calc(86 * var(--u)) calc(86 * var(--u)) calc(62 * var(--u));
}
.stat {
display: flex;
min-width: 0;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.stat strong {
color: var(--gold);
font-size: calc(56 * var(--u));
line-height: 0.95;
font-weight: 700;
}
.stat span {
width: 100%;
margin-top: calc(16 * var(--u));
color: var(--gold);
font-size: calc(30 * var(--u));
line-height: 0.95;
}
.monthly-panel {
min-height: calc(1530 * var(--u));
margin-top: calc(34 * var(--u));
margin-bottom: calc(32 * var(--u));
padding: calc(126 * var(--u)) calc(78 * var(--u)) calc(70 * var(--u));
}
.monthly-panel.is-quota {
min-height: calc(1375 * var(--u));
}
.monthly-panel h2,
.rank-panel h2 {
font-size: calc(60 * var(--u));
line-height: 1;
}
.task-tabs {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: calc(38 * var(--u));
margin-top: calc(78 * var(--u));
}
.task-tab {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: calc(120 * var(--u));
padding: 0 calc(36 * var(--u));
color: var(--gold);
font-size: calc(30 * var(--u));
font-weight: 400;
line-height: 1.15;
text-align: center;
text-shadow: 0 calc(2 * var(--u)) calc(2 * var(--u)) rgba(0, 0, 0, 0.25);
background: url("./assets/tab-inactive-a.png") center / 100% 100%
no-repeat;
}
.task-tab span {
display: block;
}
.task-tab.is-active {
background-image: url("./assets/tab-active-a.png");
}
.monthly-panel.is-quota .task-tab {
background-image: url("./assets/tab-inactive-b.png");
}
.monthly-panel.is-quota .task-tab.is-active {
background-image: url("./assets/tab-active-b.png");
}
.task-pane {
display: none;
}
.task-pane.is-active {
display: block;
}
.timeline {
position: relative;
margin-top: calc(76 * var(--u));
padding-left: calc(114 * var(--u));
}
.timeline::before {
position: absolute;
top: calc(40 * var(--u));
bottom: calc(92 * var(--u));
left: calc(38 * var(--u));
width: calc(4 * var(--u));
content: "";
background: var(--gold);
}
.milestone {
position: relative;
display: flex;
min-height: calc(139 * var(--u));
flex-direction: column;
justify-content: center;
margin-bottom: calc(30 * var(--u));
padding: calc(18 * var(--u)) calc(34 * var(--u));
background: url("./assets/milestone-row.svg") center / 100% 100%
no-repeat;
}
.milestone::before {
position: absolute;
top: 50%;
left: calc(-92 * var(--u));
width: calc(41 * var(--u));
height: calc(41 * var(--u));
content: "";
transform: translateY(-50%);
background: url("./assets/timeline-node.png") center / contain no-repeat;
}
.milestone.is-current::before {
background-image: url("./assets/timeline-node-active.png");
}
.milestone h3,
.quota-card h3 {
color: var(--white);
font-size: calc(36 * var(--u));
font-weight: 700;
line-height: 1.05;
}
.milestone p,
.quota-card p {
margin-top: calc(14 * var(--u));
color: var(--gold);
font-size: calc(30 * var(--u));
line-height: 1.2;
}
.copy-task-button {
margin-top: calc(54 * var(--u));
}
.footnote {
margin-top: calc(34 * var(--u));
font-size: calc(30 * var(--u));
line-height: 1.25;
}
.quota-list {
display: grid;
gap: calc(50 * var(--u));
margin-top: calc(76 * var(--u));
}
.quota-card {
display: grid;
grid-template-columns: minmax(0, 1fr) calc(220 * var(--u));
align-items: center;
min-height: calc(169 * var(--u));
padding: calc(24 * var(--u)) calc(30 * var(--u)) calc(22 * var(--u))
calc(36 * var(--u));
background: url("./assets/task-row.svg") center / 100% 100% no-repeat;
}
.claim-button {
display: flex;
align-items: center;
justify-content: center;
width: calc(220 * var(--u));
height: calc(60 * var(--u));
color: var(--gold);
font-size: calc(30 * var(--u));
font-weight: 700;
background: url("./assets/claim-inactive.png") center / 100% 100% no-repeat;
}
.claim-button.is-ready {
background-image: url("./assets/claim-active.png");
}
.rank-panel {
min-height: calc(5124 * var(--u));
margin-top: calc(42 * var(--u));
margin-bottom: calc(28 * var(--u));
padding: calc(126 * var(--u)) calc(68 * var(--u)) calc(72 * var(--u));
background-image:
url("./assets/panel-top-b.png"), url("./assets/panel-bottom-b.png"),
url("./assets/panel-middle-b.png");
}
.rank-list {
display: grid;
gap: calc(10 * var(--u));
margin-top: calc(72 * var(--u));
}
.rank-row {
display: grid;
grid-template-columns:
calc(74 * var(--u)) calc(80 * var(--u)) calc(108 * var(--u))
minmax(0, 1fr) auto;
align-items: center;
min-height: calc(149 * var(--u));
padding: 0 calc(42 * var(--u)) 0 calc(28 * var(--u));
background: url("./assets/rank-row.svg") center / 100% 100% no-repeat;
}
.rank-no {
color: var(--white);
font-size: calc(36 * var(--u));
line-height: 1;
text-align: center;
}
.rank-medal {
width: calc(69 * var(--u));
max-height: calc(68 * var(--u));
object-fit: contain;
justify-self: center;
}
.rank-avatar {
width: calc(108 * var(--u));
height: calc(108 * var(--u));
border: calc(2 * var(--u)) solid #ffebbb;
border-radius: 999px;
object-fit: cover;
}
.rank-name {
padding-left: calc(24 * var(--u));
overflow: hidden;
color: var(--white);
font-size: calc(36 * var(--u));
font-weight: 700;
line-height: 1.1;
text-overflow: ellipsis;
white-space: nowrap;
}
.rank-score {
color: var(--white);
font-size: calc(36 * var(--u));
font-weight: 700;
line-height: 1;
white-space: nowrap;
}
[hidden] {
display: none !important;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

View File

@ -0,0 +1,93 @@
<!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>Recharge Reward</title>
<link rel="stylesheet" href="./style.css?v=20260606-cumulative-api" />
</head>
<body>
<div class="app-viewport" id="appViewport">
<main class="page" id="page">
<div class="stage-wrap" id="stageWrap">
<section
class="stage"
aria-label="Recharge Reward activity"
data-i18n-aria="rechargeReward.pageLabel"
>
<div class="language-switcher">
<button
class="language-button"
type="button"
aria-label="Change language"
aria-expanded="false"
data-language-toggle
data-current-lang
data-i18n-aria="rechargeReward.changeLanguage"
>
EN
</button>
<div class="language-menu" data-language-menu hidden>
<button type="button" data-lang-option="en">EN</button>
<button type="button" data-lang-option="ar">AR</button>
<button type="button" data-lang-option="tr">TR</button>
<button type="button" data-lang-option="es">ES</button>
<button type="button" data-lang-option="zh">ZH</button>
<button type="button" data-lang-option="id">ID</button>
</div>
</div>
<img class="bg bg-top" src="./assets/bg-top.png" alt="" />
<img class="bg bg-mid bg-mid-a" src="./assets/bg-mid.png" alt="" />
<img class="bg bg-mid bg-mid-b" src="./assets/bg-mid.png" alt="" />
<img class="bg bg-mid bg-mid-c" src="./assets/bg-mid.png" alt="" />
<img class="bg bg-bottom" src="./assets/bg-bottom.png" alt="" />
<section class="countdown" aria-label="Activity countdown">
<img class="countdown-frame" src="./assets/countdown-frame.png" alt="" />
<strong class="count-number" data-count="days">01</strong>
<strong class="count-number" data-count="hours">01</strong>
<strong class="count-number" data-count="minutes">01</strong>
<strong class="count-number" data-count="seconds">01</strong>
<span class="count-label" data-i18n="rechargeReward.days">Days</span>
<span class="count-label" data-i18n="rechargeReward.hours">Hours</span>
<span class="count-label" data-i18n="rechargeReward.minutes">Minutes</span>
<span class="count-label" data-i18n="rechargeReward.seconds">Seconds</span>
</section>
<section class="profile-card" aria-label="My recharge status">
<img class="profile-frame" src="./assets/profile-frame.png" alt="" />
<img class="profile-avatar" src="./assets/profile-avatar.png" alt="" />
<h1 class="profile-name">nanfangjian</h1>
<p class="profile-id">ID: 12345678</p>
<p class="profile-recharge">Accumulated Recharge:$59.98</p>
</section>
<div class="reward-sections" id="rewardSections"></div>
</section>
</div>
</main>
</div>
<script>
(function () {
try {
var params = new URLSearchParams(window.location.search);
if (
!params.get('lang') &&
!window.localStorage.getItem('recharge_reward_lang_ready')
) {
window.localStorage.setItem('hyapp_h5_lang', 'en');
window.localStorage.setItem('recharge_reward_lang_ready', '1');
}
} catch (error) {
document.documentElement.lang = 'en';
}
})();
</script>
<script src="../../common/i18n.js?v=20260606-cumulative"></script>
<script src="../../common/api.js?v=20260606-cumulative"></script>
<script src="./script.js?v=20260606-cumulative-api"></script>
</body>
</html>

View File

@ -0,0 +1,368 @@
(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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
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();
})();

View File

@ -0,0 +1,484 @@
* {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
:root {
--app-scale: 1;
--design-scale: 0.3472222222;
}
html,
body {
margin: 0;
width: 100%;
min-height: 100%;
overflow-x: hidden;
background: #1b100d;
color: #f6e0b2;
font-family:
Arial,
"Helvetica Neue",
Helvetica,
sans-serif;
}
button {
border: 0;
padding: 0;
background: transparent;
color: inherit;
font: inherit;
cursor: pointer;
outline: none;
}
.app-viewport {
position: relative;
width: 375px;
min-height: 1250px;
margin: 0 auto;
transform: scale(var(--app-scale));
transform-origin: top center;
}
.page {
position: relative;
width: 375px;
min-height: 1250px;
overflow: hidden;
background: #1b100d;
}
.stage-wrap {
position: relative;
width: 375px;
height: 1250px;
overflow: hidden;
}
.stage {
position: absolute;
left: 0;
top: 0;
width: 1080px;
height: 3600px;
overflow: hidden;
transform: scale(var(--design-scale));
transform-origin: top left;
background: #1b100d;
}
.stage > * {
position: absolute;
z-index: 1;
}
.language-switcher {
position: absolute;
z-index: 10;
top: 44px;
right: 38px;
width: 190px;
direction: ltr;
}
.language-button {
display: flex;
align-items: center;
justify-content: center;
width: 156px;
height: 82px;
margin-left: auto;
border: 2px solid rgba(255, 232, 184, 0.62);
border-radius: 999px;
background: rgba(25, 9, 1, 0.58);
box-shadow:
inset 0 0 18px rgba(255, 210, 93, 0.18),
0 8px 18px rgba(0, 0, 0, 0.28);
color: #ffe8b8;
font-size: 34px;
font-weight: 800;
line-height: 34px;
letter-spacing: 0;
}
.language-menu {
position: absolute;
top: 94px;
right: 0;
width: 190px;
overflow: hidden;
border: 2px solid rgba(255, 232, 184, 0.56);
border-radius: 18px;
background: rgba(25, 9, 1, 0.92);
box-shadow: 0 16px 30px rgba(0, 0, 0, 0.34);
}
.language-menu button {
display: block;
width: 100%;
height: 58px;
color: #fff5dc;
font-size: 28px;
font-weight: 700;
line-height: 28px;
}
.language-menu button.is-active {
background: rgba(255, 210, 93, 0.22);
color: #ffd25d;
}
.bg {
left: 0;
width: 1080px;
object-fit: fill;
pointer-events: none;
z-index: 0;
}
.bg-top {
top: 0;
height: 1588px;
}
.bg-mid {
height: 649px;
}
.bg-mid-a {
top: 1476px;
}
.bg-mid-b {
top: 2050px;
}
.bg-mid-c {
top: 2600px;
}
.bg-bottom {
top: 3022px;
height: 578px;
}
.countdown {
left: 24px;
top: 696px;
width: 1032px;
height: 344px;
}
.countdown-frame {
position: absolute;
inset: 0;
width: 1032px;
height: 344px;
object-fit: fill;
pointer-events: none;
}
.count-number {
position: absolute;
top: 148px;
width: 96px;
height: 74px;
color: #ffce95;
font-size: 60px;
font-weight: 800;
line-height: 74px;
text-align: center;
text-shadow:
0 2px 0 #47200d,
0 0 14px rgba(255, 210, 135, 0.58);
}
.count-number[data-count="days"] {
left: 165px;
}
.count-number[data-count="hours"] {
left: 365px;
}
.count-number[data-count="minutes"] {
left: 570px;
}
.count-number[data-count="seconds"] {
left: 770px;
}
.count-label {
position: absolute;
top: 239px;
width: 150px;
height: 30px;
color: #100800;
font-size: 22px;
font-weight: 800;
line-height: 30px;
text-align: center;
white-space: nowrap;
transform: translateX(-50%);
}
.count-label:nth-of-type(1) {
left: 213px;
}
.count-label:nth-of-type(2) {
left: 413px;
}
.count-label:nth-of-type(3) {
left: 618px;
}
.count-label:nth-of-type(4) {
left: 818px;
}
.profile-card {
left: 43px;
top: 1040px;
width: 996px;
height: 659px;
}
.profile-frame {
position: absolute;
inset: 0;
width: 996px;
height: 659px;
object-fit: fill;
pointer-events: none;
}
.profile-avatar {
position: absolute;
left: 381px;
top: 147px;
width: 232px;
height: 232px;
border-radius: 50%;
object-fit: cover;
pointer-events: none;
}
.profile-name,
.profile-id,
.profile-recharge {
position: absolute;
left: 120px;
width: 756px;
margin: 0;
color: #f6e0b2;
text-align: center;
text-shadow: 0 3px 8px rgba(0, 0, 0, 0.72);
}
.profile-name {
top: 416px;
font-size: 38px;
font-weight: 800;
line-height: 48px;
}
.profile-id {
top: 472px;
font-size: 26px;
font-weight: 700;
line-height: 34px;
}
.profile-recharge {
top: 514px;
font-size: 36px;
font-weight: 800;
line-height: 46px;
}
.reward-sections {
left: 0;
top: 0;
width: 1080px;
height: 3600px;
}
.reward-section {
position: absolute;
left: 0;
top: var(--section-top);
width: 1080px;
}
.title-pill {
position: absolute;
left: 234px;
top: 0;
width: 612px;
height: 204px;
z-index: 4;
}
.title-pill img {
position: absolute;
inset: 0;
width: 612px;
height: 204px;
object-fit: fill;
pointer-events: none;
}
.title-pill span {
position: absolute;
left: 0;
top: 78px;
width: 612px;
color: #f6e0b2;
font-size: 40px;
font-weight: 900;
line-height: 48px;
text-align: center;
text-shadow:
0 2px 0 #4f2306,
0 0 14px rgba(255, 207, 113, 0.45);
}
.reward-panel {
position: absolute;
left: 82px;
top: var(--panel-top);
width: 915px;
overflow: hidden;
z-index: 1;
}
.reward-panel-large {
height: 497px;
}
.reward-panel-small {
height: 469px;
}
.reward-panel-frame {
position: absolute;
inset: 0;
width: 915px;
height: 497px;
object-fit: fill;
pointer-events: none;
}
.reward-panel-small .reward-panel-frame {
height: 497px;
}
.reward-card {
position: absolute;
color: #f6e0b2;
text-align: center;
z-index: 3;
}
.reward-card.is-granted .reward-frame {
filter: drop-shadow(0 0 18px rgba(255, 239, 173, 0.72));
}
.reward-frame {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: fill;
pointer-events: none;
}
.reward-icon {
position: absolute;
object-fit: contain;
pointer-events: none;
}
.reward-price {
position: absolute;
left: 0;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #f6e0b2;
font-weight: 900;
text-align: center;
text-shadow:
0 2px 0 #3a1704,
0 0 10px rgba(255, 207, 113, 0.44);
}
.reward-card-large {
width: 327px;
height: 327px;
}
.reward-card-large .reward-icon {
left: 67px;
top: 53px;
width: 193px;
height: 193px;
}
.reward-card-large .reward-price {
top: 246px;
height: 52px;
font-size: 30px;
line-height: 36px;
}
.reward-card-small {
width: 234px;
height: 234px;
}
.reward-card-small .reward-icon {
left: 48px;
top: 38px;
width: 138px;
height: 138px;
}
.reward-card-small .reward-price {
top: 175px;
height: 40px;
font-size: 22px;
line-height: 28px;
}
.reward-card-compact {
width: 210px;
height: 210px;
}
.reward-card-compact .reward-icon {
left: 43px;
top: 34px;
width: 124px;
height: 124px;
}
.reward-card-compact .reward-price {
top: 157px;
height: 36px;
font-size: 20px;
line-height: 26px;
}
@media (min-width: 431px) {
html,
body {
background: #100806;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,174 @@
<!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>Room Reward Event</title>
<link rel="stylesheet" href="../../common/theme.css?v=20260606c" />
<link rel="stylesheet" href="./style.css?v=20260606c" />
</head>
<body>
<div class="app-viewport" id="appViewport">
<main class="page" id="page">
<div class="stage-wrap" id="stageWrap">
<section
class="stage"
aria-label="Room Reward Event"
data-i18n-aria="roomReward.pageLabel"
>
<div class="language-switcher">
<button
class="language-button"
type="button"
aria-label="Change language"
aria-expanded="false"
data-language-toggle
data-current-lang
data-i18n-aria="roomReward.changeLanguage"
>
EN
</button>
<div class="language-menu" data-language-menu hidden>
<button type="button" data-lang-option="en">EN</button>
<button type="button" data-lang-option="ar">AR</button>
<button type="button" data-lang-option="tr">TR</button>
<button type="button" data-lang-option="es">ES</button>
<button type="button" data-lang-option="zh">ZH</button>
<button type="button" data-lang-option="id">ID</button>
</div>
</div>
<img
class="hero-bg"
src="./assets/296_8271.png"
alt=""
aria-hidden="true"
/>
<section
class="target-card"
aria-label="Current reward target"
data-i18n-aria="roomReward.currentTarget"
>
<img
class="target-card-img"
src="./assets/296_8274.png"
alt=""
aria-hidden="true"
/>
<div class="target-value">
<img
class="coin-icon"
src="./assets/296_8275.png"
alt=""
aria-hidden="true"
/>
<strong id="targetReward">1000</strong>
</div>
</section>
<section
class="reward-table"
aria-label="Room reward tiers"
data-i18n-aria="roomReward.tiersLabel"
>
<div class="table-fill" aria-hidden="true">
<div class="table-row-bg row-dark"></div>
<div class="table-row-bg row-light"></div>
<div class="table-row-bg row-dark"></div>
<div class="table-row-bg row-light"></div>
<div class="table-row-bg row-dark"></div>
<div class="table-row-bg row-light"></div>
<div class="table-row-bg row-dark"></div>
<div class="table-row-bg row-light"></div>
</div>
<img
class="table-border table-border-side"
src="./assets/296_8350.png"
alt=""
aria-hidden="true"
/>
<img
class="table-border table-border-top"
src="./assets/296_8301.png"
alt=""
aria-hidden="true"
/>
<img
class="table-border table-border-bottom"
src="./assets/296_8326.png"
alt=""
aria-hidden="true"
/>
<div class="table-divider" aria-hidden="true"></div>
<div class="table-content" id="tierTable"></div>
</section>
<section
class="user-panel"
aria-label="My room reward progress"
data-i18n-aria="roomReward.progressLabel"
>
<img class="user-avatar" id="userAvatar" src="./assets/294_8091.png" alt="" />
<div class="user-name" id="userName">Namename...</div>
<div class="user-id" id="userId">ID: 123456789</div>
<div class="metric-label contribution-label" data-i18n="roomReward.contribution">
This week's contribution:
</div>
<div class="metric-label rewards-label" data-i18n="roomReward.rewards">
This week's rewards:
</div>
<button
class="reward-button is-active"
id="contributionButton"
type="button"
aria-label="Contribution reward"
data-i18n-aria="roomReward.contributionReward"
data-active-src="./assets/296_8424.png"
data-inactive-src="./assets/296_8429.png"
>
<img src="./assets/296_8424.png" alt="" aria-hidden="true" />
<span id="contributionValue">100</span>
</button>
<button
class="reward-button is-disabled"
id="rewardsButton"
type="button"
aria-label="Weekly rewards"
data-i18n-aria="roomReward.weeklyRewards"
disabled
data-active-src="./assets/296_8424.png"
data-inactive-src="./assets/296_8429.png"
>
<img src="./assets/296_8429.png" alt="" aria-hidden="true" />
<span id="weeklyRewardValue">100</span>
</button>
</section>
</section>
</div>
</main>
</div>
<script>
(function () {
try {
var params = new URLSearchParams(window.location.search);
if (
!params.get('lang') &&
!window.localStorage.getItem('room_reward_lang_ready')
) {
window.localStorage.setItem('hyapp_h5_lang', 'en');
window.localStorage.setItem('room_reward_lang_ready', '1');
}
} catch (error) {
document.documentElement.lang = 'en';
}
})();
</script>
<script src="../../common/i18n.js?v=20260606c"></script>
<script src="../../common/api.js?v=20260606c"></script>
<script src="./script.js?v=20260606c"></script>
</body>
</html>

View File

@ -0,0 +1,257 @@
(function () {
var viewport = document.getElementById('appViewport');
var table = document.getElementById('tierTable');
var currentData = null;
var mockData = {
targetReward: '1000',
user: {
name: 'Namename...',
id: '123456789',
avatar: './assets/294_8091.png',
contributionReward: '100',
weeklyReward: '100',
canClaimContribution: true,
canClaimWeeklyReward: false,
},
tiers: [
{ turnover: '7M', reward: '350k' },
{ turnover: '7M', reward: '350k' },
{ turnover: '7M', reward: '350k' },
{ turnover: '7M', reward: '350k' },
{ turnover: '7M', reward: '350k' },
{ turnover: '7M', reward: '350k' },
{ turnover: '7M', reward: '350k' },
],
};
function escapeHTML(value) {
return String(value == null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function t(key, fallback) {
if (window.HyAppI18n && window.HyAppI18n.t) {
return window.HyAppI18n.t(key, fallback);
}
return fallback;
}
function syncStaticText() {
document.title = t('roomReward.pageTitle', 'Room Reward Event');
}
function userIDText(value) {
return t('roomReward.idPrefix', 'ID:') + ' ' + value;
}
function renderTable(data) {
data = data || mockData;
var rows = [
'<div class="tier-row is-head">',
'<div data-i18n="roomReward.turnover">',
t('roomReward.turnover', 'Room turnover'),
'</div>',
'<div data-i18n="roomReward.receiveReward">',
t('roomReward.receiveReward', 'Receive a reward'),
'</div>',
'</div>',
];
(data.tiers || []).forEach(function (item) {
rows.push(
[
'<div class="tier-row">',
'<div>',
escapeHTML(item.turnover),
'</div>',
'<div class="tier-reward">',
'<span>',
escapeHTML(item.reward),
'</span>',
'<img class="mini-coin" src="./assets/296_8275.png" alt="" aria-hidden="true" />',
'</div>',
'</div>',
].join('')
);
});
table.innerHTML = rows.join('');
if (window.HyAppI18n && window.HyAppI18n.apply) {
window.HyAppI18n.apply(table);
}
}
function renderUser(data) {
data = data || mockData;
document.getElementById('targetReward').textContent = data.targetReward;
document.getElementById('userAvatar').src = data.user.avatar;
document.getElementById('userName').textContent =
data.user.name || t('roomReward.roomOwner', 'Room owner');
document.getElementById('userId').textContent = userIDText(data.user.id);
document.getElementById('contributionValue').textContent =
data.user.contributionReward;
document.getElementById('weeklyRewardValue').textContent =
data.user.weeklyReward;
var contributionButton = document.getElementById('contributionButton');
var rewardsButton = document.getElementById('rewardsButton');
setButtonState(
contributionButton,
Boolean(data.user.canClaimContribution)
);
setButtonState(rewardsButton, Boolean(data.user.canClaimWeeklyReward));
}
function setButtonState(button, enabled) {
var img = button.querySelector('img');
button.disabled = !enabled;
button.classList.toggle('is-disabled', !enabled);
button.classList.toggle('is-active', enabled);
if (img) {
img.src = enabled
? button.getAttribute('data-active-src')
: button.getAttribute('data-inactive-src');
}
}
function updateBodyHeight() {
var scale = Math.min(window.innerWidth / 375, 1);
document.documentElement.style.setProperty('--app-scale', String(scale));
document.body.style.minHeight = viewport.offsetHeight * scale + 'px';
}
function readQuery(name) {
return new URLSearchParams(window.location.search).get(name);
}
function shouldUseMock() {
return (
readQuery('mock') === '1' ||
window.location.protocol === 'file:' ||
!window.HyAppAPI
);
}
function compactNumber(value) {
var number = Number(value || 0);
if (!Number.isFinite(number)) return '0';
if (number >= 1000000) return trimFixed(number / 1000000) + 'M';
if (number >= 1000) return trimFixed(number / 1000) + 'k';
return String(Math.floor(number));
}
function trimFixed(value) {
var fixed = value >= 10 ? value.toFixed(0) : value.toFixed(1);
return fixed.replace(/\.0$/, '');
}
function valueFrom(source) {
for (var index = 1; index < arguments.length; index += 1) {
var key = arguments[index];
if (
source &&
source[key] !== undefined &&
source[key] !== null &&
source[key] !== ''
) {
return source[key];
}
}
return '';
}
function normalizeAPIData(payload) {
var data = payload || {};
var user = data.user || {};
var room = data.room || {};
var tiers = Array.isArray(data.tiers) ? data.tiers : [];
var currentCoinSpent = Number(
valueFrom(data, 'currentCoinSpent', 'current_coin_spent') || 0
);
var expectedReward = Number(
valueFrom(
data,
'expectedRewardCoinAmount',
'expected_reward_coin_amount'
) || 0
);
return {
targetReward: compactNumber(expectedReward),
user: {
name:
valueFrom(user, 'username', 'name') ||
valueFrom(room, 'title'),
id:
valueFrom(user, 'displayUserId', 'display_user_id') ||
valueFrom(user, 'userId', 'user_id') ||
valueFrom(room, 'ownerUserId', 'owner_user_id') ||
'-',
avatar:
valueFrom(user, 'avatar') ||
valueFrom(room, 'coverUrl', 'cover_url') ||
'./assets/294_8091.png',
contributionReward: compactNumber(currentCoinSpent),
weeklyReward: compactNumber(expectedReward),
canClaimContribution: currentCoinSpent > 0,
canClaimWeeklyReward: expectedReward > 0,
},
tiers: tiers.map(function (tier) {
return {
turnover: compactNumber(
valueFrom(
tier,
'thresholdCoinSpent',
'threshold_coin_spent'
)
),
reward: compactNumber(
valueFrom(tier, 'rewardCoinAmount', 'reward_coin_amount')
),
};
}),
};
}
function loadData() {
if (shouldUseMock()) {
return Promise.resolve(mockData);
}
return window.HyAppAPI.get('/api/v1/activities/room-turnover-reward')
.then(normalizeAPIData)
.catch(function (error) {
console.warn('[room-reward] api fallback', error);
return mockData;
});
}
function render(data) {
currentData = data || mockData;
renderTable(currentData);
renderUser(currentData);
}
function init() {
syncStaticText();
render(mockData);
loadData().then(render);
window.addEventListener('resize', updateBodyHeight);
window.addEventListener('hyapp:i18n-ready', function () {
syncStaticText();
renderTable(currentData || mockData);
renderUser(currentData || mockData);
});
updateBodyHeight();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();

View File

@ -0,0 +1,422 @@
* {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
:root {
--app-scale: 1;
--design-scale: 0.3472222222;
}
html,
body {
margin: 0;
width: 100%;
min-height: 100%;
overflow-x: hidden;
background: #291000;
color: #fff;
font-family:
Arial,
"Helvetica Neue",
Helvetica,
sans-serif;
}
button {
border: 0;
padding: 0;
background: transparent;
color: inherit;
font: inherit;
outline: none;
}
[hidden] {
display: none !important;
}
.app-viewport {
position: relative;
width: 375px;
min-height: 772px;
margin: 0 auto;
transform: scale(var(--app-scale));
transform-origin: top center;
}
.page {
position: relative;
width: 375px;
min-height: 772px;
overflow: hidden;
background: #291000;
}
.stage-wrap {
position: relative;
width: 375px;
height: 772px;
overflow: hidden;
}
.stage {
position: absolute;
left: 0;
top: 0;
width: 1080px;
height: 2221px;
overflow: hidden;
transform: scale(var(--design-scale));
transform-origin: top left;
background: #291000;
}
.language-switcher {
position: absolute;
z-index: 10;
top: 44px;
right: 38px;
width: 190px;
direction: ltr;
}
.language-button {
display: flex;
align-items: center;
justify-content: center;
width: 156px;
height: 82px;
margin-left: auto;
border: 2px solid rgba(255, 232, 184, 0.62);
border-radius: 999px;
background: rgba(25, 9, 1, 0.58);
box-shadow:
inset 0 0 18px rgba(255, 210, 93, 0.18),
0 8px 18px rgba(0, 0, 0, 0.28);
color: #ffe8b8;
font-size: 34px;
font-weight: 800;
line-height: 34px;
letter-spacing: 0;
cursor: pointer;
}
.language-menu {
position: absolute;
top: 94px;
right: 0;
width: 190px;
overflow: hidden;
border: 2px solid rgba(255, 232, 184, 0.56);
border-radius: 18px;
background: rgba(25, 9, 1, 0.92);
box-shadow: 0 16px 30px rgba(0, 0, 0, 0.34);
}
.language-menu button {
display: block;
width: 100%;
height: 58px;
color: #fff5dc;
font-size: 28px;
font-weight: 700;
line-height: 28px;
cursor: pointer;
}
.language-menu button.is-active {
background: rgba(255, 210, 93, 0.22);
color: #ffd25d;
}
.hero-bg {
position: absolute;
left: 0;
top: 0;
width: 1080px;
height: 1919px;
object-fit: cover;
pointer-events: none;
}
.target-card {
position: absolute;
left: 47px;
top: 892px;
width: 985px;
height: 319px;
}
.target-card-img {
position: absolute;
inset: 0;
width: 985px;
height: 319px;
object-fit: fill;
pointer-events: none;
}
.target-value {
position: absolute;
left: 395px;
top: 111px;
display: flex;
align-items: center;
justify-content: center;
width: 238px;
height: 96px;
gap: 14px;
color: #fff1d6;
}
.coin-icon {
width: 44px;
height: 44px;
object-fit: contain;
}
.target-value strong {
font-size: 80px;
font-weight: 500;
line-height: 80px;
letter-spacing: 0;
color: #ffe6ba;
}
.reward-table {
position: absolute;
left: 36px;
top: 1241px;
width: 1008px;
height: 811px;
}
.table-fill {
position: absolute;
left: 20px;
top: 15px;
width: 968px;
height: 824px;
overflow: hidden;
}
.table-row-bg {
width: 968px;
height: 103px;
}
.row-dark {
background: #190901;
}
.row-light {
background: #290e00;
}
.table-border {
position: absolute;
pointer-events: none;
object-fit: fill;
}
.table-border-side {
left: 0;
top: 0;
width: 1008px;
height: 811px;
}
.table-border-top {
left: 0;
top: 0;
width: 1008px;
height: 119px;
}
.table-border-bottom {
left: 0;
top: 736px;
width: 1008px;
height: 119px;
}
.table-divider {
position: absolute;
left: 503px;
top: 15px;
width: 2px;
height: 827px;
background: rgba(255, 255, 255, 0.92);
pointer-events: none;
}
.table-content {
position: absolute;
left: 20px;
top: 15px;
width: 968px;
height: 824px;
}
.tier-row {
display: grid;
grid-template-columns: 1fr 1fr;
width: 968px;
height: 103px;
color: #fff;
font-size: 30px;
font-weight: 500;
line-height: 30px;
letter-spacing: 0;
}
.tier-row > div {
display: flex;
align-items: center;
justify-content: center;
min-width: 0;
}
.tier-row.is-head {
height: 103px;
}
.tier-row.is-head > div {
padding-top: 4px;
}
.tier-reward {
gap: 8px;
}
.tier-reward .mini-coin {
width: 38px;
height: 38px;
object-fit: contain;
}
.user-panel {
position: absolute;
left: 0;
top: 2052px;
width: 1080px;
height: 169px;
background: #190901;
}
.user-avatar {
position: absolute;
left: 46px;
top: 25px;
width: 119px;
height: 119px;
object-fit: cover;
border-radius: 50%;
}
.user-name {
position: absolute;
left: 189px;
top: 36px;
width: 280px;
overflow: hidden;
color: #fff;
font-size: 40px;
font-weight: 500;
line-height: 42px;
letter-spacing: 0;
white-space: nowrap;
text-overflow: ellipsis;
}
.user-id {
position: absolute;
left: 189px;
top: 102px;
width: 292px;
overflow: hidden;
color: rgba(255, 255, 255, 0.7);
font-size: 30px;
font-weight: 400;
line-height: 36px;
letter-spacing: 0;
white-space: nowrap;
text-overflow: ellipsis;
}
.metric-label {
position: absolute;
overflow: hidden;
color: #fff;
font-size: 28px;
font-weight: 400;
line-height: 32px;
letter-spacing: 0;
white-space: nowrap;
text-overflow: ellipsis;
}
.contribution-label {
left: 494px;
top: 43px;
width: 328px;
}
.rewards-label {
left: 552px;
top: 111px;
width: 270px;
}
.reward-button {
position: absolute;
width: 213px;
height: 69px;
cursor: pointer;
}
.reward-button img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: fill;
pointer-events: none;
}
.reward-button span {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
color: #fff;
font-size: 24px;
font-weight: 500;
line-height: 24px;
letter-spacing: 0;
text-align: center;
}
#contributionButton {
left: 838px;
top: 19px;
}
#rewardsButton {
left: 837px;
top: 87px;
}
.reward-button.is-disabled {
cursor: default;
}
html[dir="rtl"] .metric-label,
html[dir="rtl"] .user-name,
html[dir="rtl"] .user-id {
direction: rtl;
}

Some files were not shown because too many files have changed in this diff Show More