h5活动相关
BIN
activity/invite/assets/button-active-wide.png
Normal file
|
After Width: | Height: | Size: 712 KiB |
BIN
activity/invite/assets/button-inactive-wide.png
Normal file
|
After Width: | Height: | Size: 572 KiB |
BIN
activity/invite/assets/claim-active.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
activity/invite/assets/claim-inactive.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
activity/invite/assets/countdown-panel.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
activity/invite/assets/gold-stack.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
activity/invite/assets/hero-bg.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
12
activity/invite/assets/link-input.svg
Normal 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 |
BIN
activity/invite/assets/medal-1.png
Normal file
|
After Width: | Height: | Size: 346 KiB |
BIN
activity/invite/assets/medal-2.png
Normal file
|
After Width: | Height: | Size: 319 KiB |
BIN
activity/invite/assets/medal-3.png
Normal file
|
After Width: | Height: | Size: 344 KiB |
3
activity/invite/assets/milestone-row.svg
Normal 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 |
BIN
activity/invite/assets/panel-bottom-a.png
Normal file
|
After Width: | Height: | Size: 763 KiB |
BIN
activity/invite/assets/panel-bottom-b.png
Normal file
|
After Width: | Height: | Size: 763 KiB |
BIN
activity/invite/assets/panel-middle-a.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
activity/invite/assets/panel-middle-b.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
activity/invite/assets/panel-top-a.png
Normal file
|
After Width: | Height: | Size: 748 KiB |
BIN
activity/invite/assets/panel-top-b.png
Normal file
|
After Width: | Height: | Size: 748 KiB |
3
activity/invite/assets/plus.svg
Normal 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 |
3
activity/invite/assets/rank-avatar-mask.svg
Normal 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 |
BIN
activity/invite/assets/rank-avatar.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
3
activity/invite/assets/rank-row.svg
Normal 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 |
BIN
activity/invite/assets/reward-card.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
activity/invite/assets/reward-orb-bg.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
activity/invite/assets/reward-panel-bg.png
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
activity/invite/assets/tab-active-a.png
Normal file
|
After Width: | Height: | Size: 384 KiB |
BIN
activity/invite/assets/tab-active-b.png
Normal file
|
After Width: | Height: | Size: 384 KiB |
BIN
activity/invite/assets/tab-inactive-a.png
Normal file
|
After Width: | Height: | Size: 320 KiB |
BIN
activity/invite/assets/tab-inactive-b.png
Normal file
|
After Width: | Height: | Size: 320 KiB |
BIN
activity/invite/assets/task-row.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
3
activity/invite/assets/task-row.svg
Normal 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 |
BIN
activity/invite/assets/timeline-node-active.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
activity/invite/assets/timeline-node.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
351
activity/invite/index.html
Normal 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
@ -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
@ -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;
|
||||
}
|
||||
BIN
activity/recharge-reward/assets/287_2480.png
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
activity/recharge-reward/assets/287_2483.png
Normal file
|
After Width: | Height: | Size: 956 KiB |
BIN
activity/recharge-reward/assets/287_2877.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
activity/recharge-reward/assets/287_2887.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
activity/recharge-reward/assets/287_2888.png
Normal file
|
After Width: | Height: | Size: 346 KiB |
BIN
activity/recharge-reward/assets/287_3031.png
Normal file
|
After Width: | Height: | Size: 250 KiB |
BIN
activity/recharge-reward/assets/287_3032.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
activity/recharge-reward/assets/287_3036.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
activity/recharge-reward/assets/287_3040.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
activity/recharge-reward/assets/287_3044.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
activity/recharge-reward/assets/287_3048.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
activity/recharge-reward/assets/287_3051.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
activity/recharge-reward/assets/287_3054.png
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
activity/recharge-reward/assets/287_3058.png
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
activity/recharge-reward/assets/287_3061.png
Normal file
|
After Width: | Height: | Size: 263 KiB |
BIN
activity/recharge-reward/assets/bg-bottom.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
activity/recharge-reward/assets/bg-mid.png
Normal file
|
After Width: | Height: | Size: 956 KiB |
BIN
activity/recharge-reward/assets/bg-top.png
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
activity/recharge-reward/assets/countdown-frame.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
activity/recharge-reward/assets/profile-avatar.png
Normal file
|
After Width: | Height: | Size: 346 KiB |
BIN
activity/recharge-reward/assets/profile-frame.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
activity/recharge-reward/assets/reward-card-frame-large.png
Normal file
|
After Width: | Height: | Size: 250 KiB |
BIN
activity/recharge-reward/assets/reward-card-frame.png
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
activity/recharge-reward/assets/reward-panel-frame.png
Normal file
|
After Width: | Height: | Size: 263 KiB |
BIN
activity/recharge-reward/assets/reward-ring-amber.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
activity/recharge-reward/assets/reward-ring-blue.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
activity/recharge-reward/assets/reward-ring-gold.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
activity/recharge-reward/assets/reward-ring-purple.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
activity/recharge-reward/assets/reward-ring-white.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
activity/recharge-reward/assets/section-title-frame.png
Normal file
|
After Width: | Height: | Size: 340 KiB |
93
activity/recharge-reward/index.html
Normal 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>
|
||||
368
activity/recharge-reward/script.js
Normal 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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function init() {
|
||||
var params = getParams();
|
||||
document.title = t('rechargeReward.pageTitle', 'Recharge Reward');
|
||||
fetchStatus(params).then(function (data) {
|
||||
applyData(data, params);
|
||||
});
|
||||
window.addEventListener('resize', updateBodyHeight);
|
||||
window.addEventListener('hyapp:i18n-ready', function () {
|
||||
document.title = t('rechargeReward.pageTitle', 'Recharge Reward');
|
||||
fetchStatus(params).then(function (data) {
|
||||
applyData(data, params);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
})();
|
||||
484
activity/recharge-reward/style.css
Normal 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;
|
||||
}
|
||||
}
|
||||
BIN
activity/room-reward/assets/294_8091.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
activity/room-reward/assets/296_8264.png
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
BIN
activity/room-reward/assets/296_8265.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
activity/room-reward/assets/296_8271.png
Normal file
|
After Width: | Height: | Size: 3.0 MiB |
BIN
activity/room-reward/assets/296_8274.png
Normal file
|
After Width: | Height: | Size: 425 KiB |
BIN
activity/room-reward/assets/296_8275.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
activity/room-reward/assets/296_8301.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
activity/room-reward/assets/296_8326.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
activity/room-reward/assets/296_8350.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
activity/room-reward/assets/296_8357.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
activity/room-reward/assets/296_8362.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
activity/room-reward/assets/296_8368.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
activity/room-reward/assets/296_8373.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
activity/room-reward/assets/296_8380.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
activity/room-reward/assets/296_8385.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
activity/room-reward/assets/296_8391.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
activity/room-reward/assets/296_8397.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
activity/room-reward/assets/296_8404.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
activity/room-reward/assets/296_8409.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
activity/room-reward/assets/296_8415.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
activity/room-reward/assets/296_8421.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
activity/room-reward/assets/296_8422.png
Normal file
|
After Width: | Height: | Size: 425 KiB |
BIN
activity/room-reward/assets/296_8424.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
activity/room-reward/assets/296_8429.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
activity/room-reward/assets/296_8434.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
activity/room-reward/assets/296_8435.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
activity/room-reward/assets/296_8436.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
activity/room-reward/assets/296_8437.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
174
activity/room-reward/index.html
Normal 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>
|
||||
257
activity/room-reward/script.js
Normal 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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
})();
|
||||
422
activity/room-reward/style.css
Normal 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;
|
||||
}
|
||||