diff --git a/activity/invite/assets/button-active-wide.png b/activity/invite/assets/button-active-wide.png
new file mode 100644
index 0000000..a9e4ddb
Binary files /dev/null and b/activity/invite/assets/button-active-wide.png differ
diff --git a/activity/invite/assets/button-inactive-wide.png b/activity/invite/assets/button-inactive-wide.png
new file mode 100644
index 0000000..efc70fe
Binary files /dev/null and b/activity/invite/assets/button-inactive-wide.png differ
diff --git a/activity/invite/assets/claim-active.png b/activity/invite/assets/claim-active.png
new file mode 100644
index 0000000..0e906f6
Binary files /dev/null and b/activity/invite/assets/claim-active.png differ
diff --git a/activity/invite/assets/claim-inactive.png b/activity/invite/assets/claim-inactive.png
new file mode 100644
index 0000000..c3232e8
Binary files /dev/null and b/activity/invite/assets/claim-inactive.png differ
diff --git a/activity/invite/assets/countdown-panel.png b/activity/invite/assets/countdown-panel.png
new file mode 100644
index 0000000..15d127a
Binary files /dev/null and b/activity/invite/assets/countdown-panel.png differ
diff --git a/activity/invite/assets/gold-stack.png b/activity/invite/assets/gold-stack.png
new file mode 100644
index 0000000..4aed35a
Binary files /dev/null and b/activity/invite/assets/gold-stack.png differ
diff --git a/activity/invite/assets/hero-bg.png b/activity/invite/assets/hero-bg.png
new file mode 100644
index 0000000..a0f30ae
Binary files /dev/null and b/activity/invite/assets/hero-bg.png differ
diff --git a/activity/invite/assets/link-input.svg b/activity/invite/assets/link-input.svg
new file mode 100644
index 0000000..c021ed4
--- /dev/null
+++ b/activity/invite/assets/link-input.svg
@@ -0,0 +1,12 @@
+
diff --git a/activity/invite/assets/medal-1.png b/activity/invite/assets/medal-1.png
new file mode 100644
index 0000000..df7b345
Binary files /dev/null and b/activity/invite/assets/medal-1.png differ
diff --git a/activity/invite/assets/medal-2.png b/activity/invite/assets/medal-2.png
new file mode 100644
index 0000000..67ad74d
Binary files /dev/null and b/activity/invite/assets/medal-2.png differ
diff --git a/activity/invite/assets/medal-3.png b/activity/invite/assets/medal-3.png
new file mode 100644
index 0000000..5ee50f6
Binary files /dev/null and b/activity/invite/assets/medal-3.png differ
diff --git a/activity/invite/assets/milestone-row.svg b/activity/invite/assets/milestone-row.svg
new file mode 100644
index 0000000..efec76f
--- /dev/null
+++ b/activity/invite/assets/milestone-row.svg
@@ -0,0 +1,3 @@
+
diff --git a/activity/invite/assets/panel-bottom-a.png b/activity/invite/assets/panel-bottom-a.png
new file mode 100644
index 0000000..56478bf
Binary files /dev/null and b/activity/invite/assets/panel-bottom-a.png differ
diff --git a/activity/invite/assets/panel-bottom-b.png b/activity/invite/assets/panel-bottom-b.png
new file mode 100644
index 0000000..56478bf
Binary files /dev/null and b/activity/invite/assets/panel-bottom-b.png differ
diff --git a/activity/invite/assets/panel-middle-a.png b/activity/invite/assets/panel-middle-a.png
new file mode 100644
index 0000000..e5a17d3
Binary files /dev/null and b/activity/invite/assets/panel-middle-a.png differ
diff --git a/activity/invite/assets/panel-middle-b.png b/activity/invite/assets/panel-middle-b.png
new file mode 100644
index 0000000..e5a17d3
Binary files /dev/null and b/activity/invite/assets/panel-middle-b.png differ
diff --git a/activity/invite/assets/panel-top-a.png b/activity/invite/assets/panel-top-a.png
new file mode 100644
index 0000000..4d10a57
Binary files /dev/null and b/activity/invite/assets/panel-top-a.png differ
diff --git a/activity/invite/assets/panel-top-b.png b/activity/invite/assets/panel-top-b.png
new file mode 100644
index 0000000..4d10a57
Binary files /dev/null and b/activity/invite/assets/panel-top-b.png differ
diff --git a/activity/invite/assets/plus.svg b/activity/invite/assets/plus.svg
new file mode 100644
index 0000000..f14c70c
--- /dev/null
+++ b/activity/invite/assets/plus.svg
@@ -0,0 +1,3 @@
+
diff --git a/activity/invite/assets/rank-avatar-mask.svg b/activity/invite/assets/rank-avatar-mask.svg
new file mode 100644
index 0000000..d352afe
--- /dev/null
+++ b/activity/invite/assets/rank-avatar-mask.svg
@@ -0,0 +1,3 @@
+
diff --git a/activity/invite/assets/rank-avatar.png b/activity/invite/assets/rank-avatar.png
new file mode 100644
index 0000000..6d0410b
Binary files /dev/null and b/activity/invite/assets/rank-avatar.png differ
diff --git a/activity/invite/assets/rank-row.svg b/activity/invite/assets/rank-row.svg
new file mode 100644
index 0000000..4e250ea
--- /dev/null
+++ b/activity/invite/assets/rank-row.svg
@@ -0,0 +1,3 @@
+
diff --git a/activity/invite/assets/reward-card.png b/activity/invite/assets/reward-card.png
new file mode 100644
index 0000000..e9cc2ab
Binary files /dev/null and b/activity/invite/assets/reward-card.png differ
diff --git a/activity/invite/assets/reward-orb-bg.png b/activity/invite/assets/reward-orb-bg.png
new file mode 100644
index 0000000..e9cc2ab
Binary files /dev/null and b/activity/invite/assets/reward-orb-bg.png differ
diff --git a/activity/invite/assets/reward-panel-bg.png b/activity/invite/assets/reward-panel-bg.png
new file mode 100644
index 0000000..f828ecd
Binary files /dev/null and b/activity/invite/assets/reward-panel-bg.png differ
diff --git a/activity/invite/assets/tab-active-a.png b/activity/invite/assets/tab-active-a.png
new file mode 100644
index 0000000..b8fb997
Binary files /dev/null and b/activity/invite/assets/tab-active-a.png differ
diff --git a/activity/invite/assets/tab-active-b.png b/activity/invite/assets/tab-active-b.png
new file mode 100644
index 0000000..b8fb997
Binary files /dev/null and b/activity/invite/assets/tab-active-b.png differ
diff --git a/activity/invite/assets/tab-inactive-a.png b/activity/invite/assets/tab-inactive-a.png
new file mode 100644
index 0000000..89481fc
Binary files /dev/null and b/activity/invite/assets/tab-inactive-a.png differ
diff --git a/activity/invite/assets/tab-inactive-b.png b/activity/invite/assets/tab-inactive-b.png
new file mode 100644
index 0000000..89481fc
Binary files /dev/null and b/activity/invite/assets/tab-inactive-b.png differ
diff --git a/activity/invite/assets/task-row.png b/activity/invite/assets/task-row.png
new file mode 100644
index 0000000..92d220d
Binary files /dev/null and b/activity/invite/assets/task-row.png differ
diff --git a/activity/invite/assets/task-row.svg b/activity/invite/assets/task-row.svg
new file mode 100644
index 0000000..b9a5891
--- /dev/null
+++ b/activity/invite/assets/task-row.svg
@@ -0,0 +1,3 @@
+
diff --git a/activity/invite/assets/timeline-node-active.png b/activity/invite/assets/timeline-node-active.png
new file mode 100644
index 0000000..82a2638
Binary files /dev/null and b/activity/invite/assets/timeline-node-active.png differ
diff --git a/activity/invite/assets/timeline-node.png b/activity/invite/assets/timeline-node.png
new file mode 100644
index 0000000..f868c99
Binary files /dev/null and b/activity/invite/assets/timeline-node.png differ
diff --git a/activity/invite/index.html b/activity/invite/index.html
new file mode 100644
index 0000000..1483838
--- /dev/null
+++ b/activity/invite/index.html
@@ -0,0 +1,351 @@
+
+
+
+
+
+ Invite Friends
+
+
+
+
+
+
+
+
+
+
+
+
+ 01
+ Days
+
+
+ 01
+ Hours
+
+
+ 01
+ Minutes
+
+
+ 01
+ Seconds
+
+
+
+
+
+ Your Reward
+
+ (Earn for every friend you invite)
+
+
+
+
+
+ +2000
+
+ Invitation received
+
+
+

+
+
+
+ +1000
+
+ Top up via friends
+
+
+
+
+
+
+
+ Start inviting people now
+
+
+ (Copy your invitation link and share it with your friends)
+
+
+
+ http://yourappbalbalanal
+
+
+ ABC123
+
+
+
+
+
+
+
+
+
+
+ 168
+
+ Total number of invitees
+
+
+
+ 01
+
+ Number of eligible voters
+
+
+
+ 01
+
+ Total amount recharged by invitees
+
+
+
+ 12354
+ My Rewards
+
+
+
+
+ Monthly Tasks
+
+
+
+
+
+
+
+
+
+ Milestone1:balabalabal
+
+
+ Total supply: 49999 coins
+
+
+
+
+ Milestone1:balabalabal
+
+
+ Total supply: 49999 coins
+
+
+
+
+ Milestone1:balabalabal
+
+
+ Total supply: 49999 coins
+
+
+
+
+ Milestone1:balabalabal
+
+
+ Total supply: 49999 coins
+
+
+
+
+ Milestone1:balabalabal
+
+
+ Total supply: 49999 coins
+
+
+
+
+
+
+
+
+
+
+
+
+ Milestone1:balabalabal
+
+
+ Invite 10 friends who have already made a
+ payment
+
+
+
+
+
+
+
+ Milestone1:balabalabal
+
+
+ Invite 10 friends who have already made a
+ payment
+
+
+
+
+
+
+
+ Milestone1:balabalabal
+
+
+ Invite 10 friends who have already made a
+ payment
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/activity/invite/script.js b/activity/invite/script.js
new file mode 100644
index 0000000..500529a
--- /dev/null
+++ b/activity/invite/script.js
@@ -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();
+ }
+})();
diff --git a/activity/invite/style.css b/activity/invite/style.css
new file mode 100644
index 0000000..000b17d
--- /dev/null
+++ b/activity/invite/style.css
@@ -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;
+}
diff --git a/activity/recharge-reward/assets/287_2480.png b/activity/recharge-reward/assets/287_2480.png
new file mode 100644
index 0000000..d37c1a2
Binary files /dev/null and b/activity/recharge-reward/assets/287_2480.png differ
diff --git a/activity/recharge-reward/assets/287_2483.png b/activity/recharge-reward/assets/287_2483.png
new file mode 100644
index 0000000..64a18cb
Binary files /dev/null and b/activity/recharge-reward/assets/287_2483.png differ
diff --git a/activity/recharge-reward/assets/287_2877.png b/activity/recharge-reward/assets/287_2877.png
new file mode 100644
index 0000000..015ac55
Binary files /dev/null and b/activity/recharge-reward/assets/287_2877.png differ
diff --git a/activity/recharge-reward/assets/287_2887.png b/activity/recharge-reward/assets/287_2887.png
new file mode 100644
index 0000000..dcc4e16
Binary files /dev/null and b/activity/recharge-reward/assets/287_2887.png differ
diff --git a/activity/recharge-reward/assets/287_2888.png b/activity/recharge-reward/assets/287_2888.png
new file mode 100644
index 0000000..6d86504
Binary files /dev/null and b/activity/recharge-reward/assets/287_2888.png differ
diff --git a/activity/recharge-reward/assets/287_3031.png b/activity/recharge-reward/assets/287_3031.png
new file mode 100644
index 0000000..b5337ac
Binary files /dev/null and b/activity/recharge-reward/assets/287_3031.png differ
diff --git a/activity/recharge-reward/assets/287_3032.png b/activity/recharge-reward/assets/287_3032.png
new file mode 100644
index 0000000..62f8001
Binary files /dev/null and b/activity/recharge-reward/assets/287_3032.png differ
diff --git a/activity/recharge-reward/assets/287_3036.png b/activity/recharge-reward/assets/287_3036.png
new file mode 100644
index 0000000..db4fd0a
Binary files /dev/null and b/activity/recharge-reward/assets/287_3036.png differ
diff --git a/activity/recharge-reward/assets/287_3040.png b/activity/recharge-reward/assets/287_3040.png
new file mode 100644
index 0000000..db4fd0a
Binary files /dev/null and b/activity/recharge-reward/assets/287_3040.png differ
diff --git a/activity/recharge-reward/assets/287_3044.png b/activity/recharge-reward/assets/287_3044.png
new file mode 100644
index 0000000..db4fd0a
Binary files /dev/null and b/activity/recharge-reward/assets/287_3044.png differ
diff --git a/activity/recharge-reward/assets/287_3048.png b/activity/recharge-reward/assets/287_3048.png
new file mode 100644
index 0000000..62f8001
Binary files /dev/null and b/activity/recharge-reward/assets/287_3048.png differ
diff --git a/activity/recharge-reward/assets/287_3051.png b/activity/recharge-reward/assets/287_3051.png
new file mode 100644
index 0000000..b2d59bd
Binary files /dev/null and b/activity/recharge-reward/assets/287_3051.png differ
diff --git a/activity/recharge-reward/assets/287_3054.png b/activity/recharge-reward/assets/287_3054.png
new file mode 100644
index 0000000..b6072ef
Binary files /dev/null and b/activity/recharge-reward/assets/287_3054.png differ
diff --git a/activity/recharge-reward/assets/287_3058.png b/activity/recharge-reward/assets/287_3058.png
new file mode 100644
index 0000000..07b6294
Binary files /dev/null and b/activity/recharge-reward/assets/287_3058.png differ
diff --git a/activity/recharge-reward/assets/287_3061.png b/activity/recharge-reward/assets/287_3061.png
new file mode 100644
index 0000000..d7daa9a
Binary files /dev/null and b/activity/recharge-reward/assets/287_3061.png differ
diff --git a/activity/recharge-reward/assets/bg-bottom.png b/activity/recharge-reward/assets/bg-bottom.png
new file mode 100644
index 0000000..b2d59bd
Binary files /dev/null and b/activity/recharge-reward/assets/bg-bottom.png differ
diff --git a/activity/recharge-reward/assets/bg-mid.png b/activity/recharge-reward/assets/bg-mid.png
new file mode 100644
index 0000000..64a18cb
Binary files /dev/null and b/activity/recharge-reward/assets/bg-mid.png differ
diff --git a/activity/recharge-reward/assets/bg-top.png b/activity/recharge-reward/assets/bg-top.png
new file mode 100644
index 0000000..d37c1a2
Binary files /dev/null and b/activity/recharge-reward/assets/bg-top.png differ
diff --git a/activity/recharge-reward/assets/countdown-frame.png b/activity/recharge-reward/assets/countdown-frame.png
new file mode 100644
index 0000000..015ac55
Binary files /dev/null and b/activity/recharge-reward/assets/countdown-frame.png differ
diff --git a/activity/recharge-reward/assets/profile-avatar.png b/activity/recharge-reward/assets/profile-avatar.png
new file mode 100644
index 0000000..6d86504
Binary files /dev/null and b/activity/recharge-reward/assets/profile-avatar.png differ
diff --git a/activity/recharge-reward/assets/profile-frame.png b/activity/recharge-reward/assets/profile-frame.png
new file mode 100644
index 0000000..dcc4e16
Binary files /dev/null and b/activity/recharge-reward/assets/profile-frame.png differ
diff --git a/activity/recharge-reward/assets/reward-card-frame-large.png b/activity/recharge-reward/assets/reward-card-frame-large.png
new file mode 100644
index 0000000..b5337ac
Binary files /dev/null and b/activity/recharge-reward/assets/reward-card-frame-large.png differ
diff --git a/activity/recharge-reward/assets/reward-card-frame.png b/activity/recharge-reward/assets/reward-card-frame.png
new file mode 100644
index 0000000..b6072ef
Binary files /dev/null and b/activity/recharge-reward/assets/reward-card-frame.png differ
diff --git a/activity/recharge-reward/assets/reward-panel-frame.png b/activity/recharge-reward/assets/reward-panel-frame.png
new file mode 100644
index 0000000..d7daa9a
Binary files /dev/null and b/activity/recharge-reward/assets/reward-panel-frame.png differ
diff --git a/activity/recharge-reward/assets/reward-ring-amber.png b/activity/recharge-reward/assets/reward-ring-amber.png
new file mode 100644
index 0000000..db4fd0a
Binary files /dev/null and b/activity/recharge-reward/assets/reward-ring-amber.png differ
diff --git a/activity/recharge-reward/assets/reward-ring-blue.png b/activity/recharge-reward/assets/reward-ring-blue.png
new file mode 100644
index 0000000..62f8001
Binary files /dev/null and b/activity/recharge-reward/assets/reward-ring-blue.png differ
diff --git a/activity/recharge-reward/assets/reward-ring-gold.png b/activity/recharge-reward/assets/reward-ring-gold.png
new file mode 100644
index 0000000..db4fd0a
Binary files /dev/null and b/activity/recharge-reward/assets/reward-ring-gold.png differ
diff --git a/activity/recharge-reward/assets/reward-ring-purple.png b/activity/recharge-reward/assets/reward-ring-purple.png
new file mode 100644
index 0000000..db4fd0a
Binary files /dev/null and b/activity/recharge-reward/assets/reward-ring-purple.png differ
diff --git a/activity/recharge-reward/assets/reward-ring-white.png b/activity/recharge-reward/assets/reward-ring-white.png
new file mode 100644
index 0000000..62f8001
Binary files /dev/null and b/activity/recharge-reward/assets/reward-ring-white.png differ
diff --git a/activity/recharge-reward/assets/section-title-frame.png b/activity/recharge-reward/assets/section-title-frame.png
new file mode 100644
index 0000000..07b6294
Binary files /dev/null and b/activity/recharge-reward/assets/section-title-frame.png differ
diff --git a/activity/recharge-reward/index.html b/activity/recharge-reward/index.html
new file mode 100644
index 0000000..d2463a9
--- /dev/null
+++ b/activity/recharge-reward/index.html
@@ -0,0 +1,93 @@
+
+
+
+
+
+ Recharge Reward
+
+
+
+
+
+
+
+
+
+
diff --git a/activity/recharge-reward/script.js b/activity/recharge-reward/script.js
new file mode 100644
index 0000000..49c1485
--- /dev/null
+++ b/activity/recharge-reward/script.js
@@ -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 [
+ '',
+ '

',
+ '
,
+ ')
',
+ '
',
+ escapeHTML(card.label),
+ '',
+ '
',
+ ].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 [
+ '',
+ '',
+ '

',
+ '
',
+ escapeHTML(
+ t('rechargeReward.rechargeTitle', 'Recharge {amount}').replace(
+ '{amount}',
+ formatUSDMinor(section.thresholdUsdMinor)
+ )
+ ),
+ '',
+ '
',
+ '',
+ '

',
+ '
',
+ section.cards
+ .map(function (card, cardIndex) {
+ return renderRewardCard(card, metrics.cardClass, count, cardIndex);
+ })
+ .join(''),
+ '',
+ ].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, ''');
+ }
+
+ 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();
+})();
diff --git a/activity/recharge-reward/style.css b/activity/recharge-reward/style.css
new file mode 100644
index 0000000..847aa9f
--- /dev/null
+++ b/activity/recharge-reward/style.css
@@ -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;
+ }
+}
diff --git a/activity/room-reward/assets/294_8091.png b/activity/room-reward/assets/294_8091.png
new file mode 100644
index 0000000..7280c0a
Binary files /dev/null and b/activity/room-reward/assets/294_8091.png differ
diff --git a/activity/room-reward/assets/296_8264.png b/activity/room-reward/assets/296_8264.png
new file mode 100644
index 0000000..ba9bb75
Binary files /dev/null and b/activity/room-reward/assets/296_8264.png differ
diff --git a/activity/room-reward/assets/296_8265.png b/activity/room-reward/assets/296_8265.png
new file mode 100644
index 0000000..053f197
Binary files /dev/null and b/activity/room-reward/assets/296_8265.png differ
diff --git a/activity/room-reward/assets/296_8271.png b/activity/room-reward/assets/296_8271.png
new file mode 100644
index 0000000..b3e3bad
Binary files /dev/null and b/activity/room-reward/assets/296_8271.png differ
diff --git a/activity/room-reward/assets/296_8274.png b/activity/room-reward/assets/296_8274.png
new file mode 100644
index 0000000..deac864
Binary files /dev/null and b/activity/room-reward/assets/296_8274.png differ
diff --git a/activity/room-reward/assets/296_8275.png b/activity/room-reward/assets/296_8275.png
new file mode 100644
index 0000000..938aa9d
Binary files /dev/null and b/activity/room-reward/assets/296_8275.png differ
diff --git a/activity/room-reward/assets/296_8301.png b/activity/room-reward/assets/296_8301.png
new file mode 100644
index 0000000..86ba10a
Binary files /dev/null and b/activity/room-reward/assets/296_8301.png differ
diff --git a/activity/room-reward/assets/296_8326.png b/activity/room-reward/assets/296_8326.png
new file mode 100644
index 0000000..31ca009
Binary files /dev/null and b/activity/room-reward/assets/296_8326.png differ
diff --git a/activity/room-reward/assets/296_8350.png b/activity/room-reward/assets/296_8350.png
new file mode 100644
index 0000000..78d4056
Binary files /dev/null and b/activity/room-reward/assets/296_8350.png differ
diff --git a/activity/room-reward/assets/296_8357.png b/activity/room-reward/assets/296_8357.png
new file mode 100644
index 0000000..d27457d
Binary files /dev/null and b/activity/room-reward/assets/296_8357.png differ
diff --git a/activity/room-reward/assets/296_8362.png b/activity/room-reward/assets/296_8362.png
new file mode 100644
index 0000000..5cc48ef
Binary files /dev/null and b/activity/room-reward/assets/296_8362.png differ
diff --git a/activity/room-reward/assets/296_8368.png b/activity/room-reward/assets/296_8368.png
new file mode 100644
index 0000000..d27457d
Binary files /dev/null and b/activity/room-reward/assets/296_8368.png differ
diff --git a/activity/room-reward/assets/296_8373.png b/activity/room-reward/assets/296_8373.png
new file mode 100644
index 0000000..8111a34
Binary files /dev/null and b/activity/room-reward/assets/296_8373.png differ
diff --git a/activity/room-reward/assets/296_8380.png b/activity/room-reward/assets/296_8380.png
new file mode 100644
index 0000000..8f2ec34
Binary files /dev/null and b/activity/room-reward/assets/296_8380.png differ
diff --git a/activity/room-reward/assets/296_8385.png b/activity/room-reward/assets/296_8385.png
new file mode 100644
index 0000000..28cd28a
Binary files /dev/null and b/activity/room-reward/assets/296_8385.png differ
diff --git a/activity/room-reward/assets/296_8391.png b/activity/room-reward/assets/296_8391.png
new file mode 100644
index 0000000..ee42666
Binary files /dev/null and b/activity/room-reward/assets/296_8391.png differ
diff --git a/activity/room-reward/assets/296_8397.png b/activity/room-reward/assets/296_8397.png
new file mode 100644
index 0000000..b5bff90
Binary files /dev/null and b/activity/room-reward/assets/296_8397.png differ
diff --git a/activity/room-reward/assets/296_8404.png b/activity/room-reward/assets/296_8404.png
new file mode 100644
index 0000000..8f2ec34
Binary files /dev/null and b/activity/room-reward/assets/296_8404.png differ
diff --git a/activity/room-reward/assets/296_8409.png b/activity/room-reward/assets/296_8409.png
new file mode 100644
index 0000000..28cd28a
Binary files /dev/null and b/activity/room-reward/assets/296_8409.png differ
diff --git a/activity/room-reward/assets/296_8415.png b/activity/room-reward/assets/296_8415.png
new file mode 100644
index 0000000..ee42666
Binary files /dev/null and b/activity/room-reward/assets/296_8415.png differ
diff --git a/activity/room-reward/assets/296_8421.png b/activity/room-reward/assets/296_8421.png
new file mode 100644
index 0000000..aad1014
Binary files /dev/null and b/activity/room-reward/assets/296_8421.png differ
diff --git a/activity/room-reward/assets/296_8422.png b/activity/room-reward/assets/296_8422.png
new file mode 100644
index 0000000..deac864
Binary files /dev/null and b/activity/room-reward/assets/296_8422.png differ
diff --git a/activity/room-reward/assets/296_8424.png b/activity/room-reward/assets/296_8424.png
new file mode 100644
index 0000000..9b4a0b8
Binary files /dev/null and b/activity/room-reward/assets/296_8424.png differ
diff --git a/activity/room-reward/assets/296_8429.png b/activity/room-reward/assets/296_8429.png
new file mode 100644
index 0000000..913514b
Binary files /dev/null and b/activity/room-reward/assets/296_8429.png differ
diff --git a/activity/room-reward/assets/296_8434.png b/activity/room-reward/assets/296_8434.png
new file mode 100644
index 0000000..9b4a0b8
Binary files /dev/null and b/activity/room-reward/assets/296_8434.png differ
diff --git a/activity/room-reward/assets/296_8435.png b/activity/room-reward/assets/296_8435.png
new file mode 100644
index 0000000..9b4a0b8
Binary files /dev/null and b/activity/room-reward/assets/296_8435.png differ
diff --git a/activity/room-reward/assets/296_8436.png b/activity/room-reward/assets/296_8436.png
new file mode 100644
index 0000000..913514b
Binary files /dev/null and b/activity/room-reward/assets/296_8436.png differ
diff --git a/activity/room-reward/assets/296_8437.png b/activity/room-reward/assets/296_8437.png
new file mode 100644
index 0000000..913514b
Binary files /dev/null and b/activity/room-reward/assets/296_8437.png differ
diff --git a/activity/room-reward/index.html b/activity/room-reward/index.html
new file mode 100644
index 0000000..772fcb1
--- /dev/null
+++ b/activity/room-reward/index.html
@@ -0,0 +1,174 @@
+
+
+
+
+
+ Room Reward Event
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
1000
+
+
+
+
+
+
+
+ Namename...
+ ID: 123456789
+
+ This week's contribution:
+
+
+ This week's rewards:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/activity/room-reward/script.js b/activity/room-reward/script.js
new file mode 100644
index 0000000..d827d25
--- /dev/null
+++ b/activity/room-reward/script.js
@@ -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, ''');
+ }
+
+ 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 = [
+ '',
+ '
',
+ t('roomReward.turnover', 'Room turnover'),
+ '
',
+ '
',
+ t('roomReward.receiveReward', 'Receive a reward'),
+ '
',
+ '
',
+ ];
+
+ (data.tiers || []).forEach(function (item) {
+ rows.push(
+ [
+ '',
+ '
',
+ escapeHTML(item.turnover),
+ '
',
+ '
',
+ '
',
+ escapeHTML(item.reward),
+ '',
+ '

',
+ '
',
+ '
',
+ ].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();
+ }
+})();
diff --git a/activity/room-reward/style.css b/activity/room-reward/style.css
new file mode 100644
index 0000000..b2239a7
--- /dev/null
+++ b/activity/room-reward/style.css
@@ -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;
+}
diff --git a/activity/weekly-star/assets/avatar-top.png b/activity/weekly-star/assets/avatar-top.png
new file mode 100644
index 0000000..a2f0811
Binary files /dev/null and b/activity/weekly-star/assets/avatar-top.png differ
diff --git a/activity/weekly-star/assets/avatar-user.png b/activity/weekly-star/assets/avatar-user.png
new file mode 100644
index 0000000..6d0410b
Binary files /dev/null and b/activity/weekly-star/assets/avatar-user.png differ
diff --git a/activity/weekly-star/assets/bg-stars.png b/activity/weekly-star/assets/bg-stars.png
new file mode 100644
index 0000000..ef2ea14
Binary files /dev/null and b/activity/weekly-star/assets/bg-stars.png differ
diff --git a/activity/weekly-star/assets/gift-ball.png b/activity/weekly-star/assets/gift-ball.png
new file mode 100644
index 0000000..4760863
Binary files /dev/null and b/activity/weekly-star/assets/gift-ball.png differ
diff --git a/activity/weekly-star/assets/gift-card.png b/activity/weekly-star/assets/gift-card.png
new file mode 100644
index 0000000..71a8683
Binary files /dev/null and b/activity/weekly-star/assets/gift-card.png differ
diff --git a/activity/weekly-star/assets/gift-glow.png b/activity/weekly-star/assets/gift-glow.png
new file mode 100644
index 0000000..68399e8
Binary files /dev/null and b/activity/weekly-star/assets/gift-glow.png differ
diff --git a/activity/weekly-star/assets/hero-bg.png b/activity/weekly-star/assets/hero-bg.png
new file mode 100644
index 0000000..930f3dd
Binary files /dev/null and b/activity/weekly-star/assets/hero-bg.png differ
diff --git a/activity/weekly-star/assets/medal-1.png b/activity/weekly-star/assets/medal-1.png
new file mode 100644
index 0000000..df7b345
Binary files /dev/null and b/activity/weekly-star/assets/medal-1.png differ
diff --git a/activity/weekly-star/assets/medal-2.png b/activity/weekly-star/assets/medal-2.png
new file mode 100644
index 0000000..67ad74d
Binary files /dev/null and b/activity/weekly-star/assets/medal-2.png differ
diff --git a/activity/weekly-star/assets/medal-3.png b/activity/weekly-star/assets/medal-3.png
new file mode 100644
index 0000000..5ee50f6
Binary files /dev/null and b/activity/weekly-star/assets/medal-3.png differ
diff --git a/activity/weekly-star/assets/reward-bg.png b/activity/weekly-star/assets/reward-bg.png
new file mode 100644
index 0000000..1602091
Binary files /dev/null and b/activity/weekly-star/assets/reward-bg.png differ
diff --git a/activity/weekly-star/assets/reward-frame-bg.png b/activity/weekly-star/assets/reward-frame-bg.png
new file mode 100644
index 0000000..d1ea9c0
Binary files /dev/null and b/activity/weekly-star/assets/reward-frame-bg.png differ
diff --git a/activity/weekly-star/assets/reward-tab-active.png b/activity/weekly-star/assets/reward-tab-active.png
new file mode 100644
index 0000000..7ec610b
Binary files /dev/null and b/activity/weekly-star/assets/reward-tab-active.png differ
diff --git a/activity/weekly-star/assets/reward-tab-inactive.png b/activity/weekly-star/assets/reward-tab-inactive.png
new file mode 100644
index 0000000..9f66463
Binary files /dev/null and b/activity/weekly-star/assets/reward-tab-inactive.png differ
diff --git a/activity/weekly-star/assets/reward-title-bar.png b/activity/weekly-star/assets/reward-title-bar.png
new file mode 100644
index 0000000..a2378d0
Binary files /dev/null and b/activity/weekly-star/assets/reward-title-bar.png differ
diff --git a/activity/weekly-star/assets/reward-title-fill.png b/activity/weekly-star/assets/reward-title-fill.png
new file mode 100644
index 0000000..c41b64d
Binary files /dev/null and b/activity/weekly-star/assets/reward-title-fill.png differ
diff --git a/activity/weekly-star/assets/tab-active.png b/activity/weekly-star/assets/tab-active.png
new file mode 100644
index 0000000..7ec610b
Binary files /dev/null and b/activity/weekly-star/assets/tab-active.png differ
diff --git a/activity/weekly-star/assets/tab-inactive.png b/activity/weekly-star/assets/tab-inactive.png
new file mode 100644
index 0000000..9f66463
Binary files /dev/null and b/activity/weekly-star/assets/tab-inactive.png differ
diff --git a/activity/weekly-star/assets/time-text-fill.png b/activity/weekly-star/assets/time-text-fill.png
new file mode 100644
index 0000000..fedb3e9
Binary files /dev/null and b/activity/weekly-star/assets/time-text-fill.png differ
diff --git a/activity/weekly-star/assets/title-bar.png b/activity/weekly-star/assets/title-bar.png
new file mode 100644
index 0000000..0b3345f
Binary files /dev/null and b/activity/weekly-star/assets/title-bar.png differ
diff --git a/activity/weekly-star/index.html b/activity/weekly-star/index.html
new file mode 100644
index 0000000..8ce8cc1
--- /dev/null
+++ b/activity/weekly-star/index.html
@@ -0,0 +1,116 @@
+
+
+
+
+
+ Weekly Star
+
+
+
+
+
+
+
+
+
+
+
+

+
+ 2025-12-28 00:00-2026-1-3 23:59(utc+3)
+
+
+
+
+ Note: Send the following three gifts to participate in the
+ ranking. Users who rank in the top 3 of the leaderboard
+ will receive rewards.
+
+
+
+
+
+
+
+ 2025/12/07-2025/12/14
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/activity/weekly-star/script.js b/activity/weekly-star/script.js
new file mode 100644
index 0000000..344717a
--- /dev/null
+++ b/activity/weekly-star/script.js
@@ -0,0 +1,362 @@
+(function () {
+ var page = document.getElementById('page');
+ var viewport = document.getElementById('appViewport');
+ var tabs = Array.prototype.slice.call(document.querySelectorAll('[data-tab-target]'));
+ var panels = Array.prototype.slice.call(document.querySelectorAll('[data-panel]'));
+ var stageWrap = document.getElementById('stageWrap');
+ var dateText = document.querySelector('.date-text');
+ var note = document.querySelector('.note');
+ var historyDate = document.querySelector('.history-date');
+
+ var mockRanks = [
+ { rank: 1, name: 'hanneem', score: '337700', avatar: './assets/avatar-top.png' },
+ { rank: 2, name: 'hanneem', score: '337700', avatar: './assets/avatar-user.png' },
+ { rank: 3, name: 'hanneem', score: '337700', avatar: './assets/avatar-user.png' },
+ { rank: 4, name: 'hanneem', score: '337700', avatar: './assets/avatar-user.png' },
+ { rank: 5, name: 'hanneem', score: '337700', avatar: './assets/avatar-user.png' },
+ ];
+ var mockRewards = [
+ { rank_no: 1, resource_group_id: 1001, top: 1488 },
+ { rank_no: 2, resource_group_id: 1002, top: 1985 },
+ { rank_no: 3, resource_group_id: 1003, top: 2482 },
+ ];
+ var state = {
+ current: null,
+ weeklyEntries: [],
+ historyCycles: [],
+ error: '',
+ loading: true,
+ };
+
+ function rankMedal(rank) {
+ if (rank === 1) return './assets/medal-1.png';
+ if (rank === 2) return './assets/medal-2.png';
+ if (rank === 3) return './assets/medal-3.png';
+ return '';
+ }
+
+ function renderRankList(targetID, top, items, emptyText) {
+ var list = document.getElementById(targetID);
+ var rows = (items || []).slice(0, 8);
+ if (!rows.length) {
+ list.innerHTML =
+ '' + escapeHTML(emptyText || 'No ranking data yet.') + '
';
+ return;
+ }
+ list.innerHTML = rows
+ .map(function (item, index) {
+ var medal = rankMedal(item.rank);
+ return [
+ '',
+ medal
+ ? '
'
+ : '' + item.rank + '',
+ '
',
+ '',
+ escapeHTML(item.name || '-'),
+ '
',
+ '',
+ escapeHTML(formatNumber(item.score)),
+ '
',
+ '',
+ ].join('');
+ })
+ .join('');
+ }
+
+ function rewardItem(item) {
+ var img = item.image || './assets/gift-ball.png';
+ var label = item.label || 'Resource Group #' + (item.resource_group_id || '-');
+ return [
+ '',
+ '
',
+ '
',
+ escapeHTML(label),
+ '
',
+ '
',
+ ].join('');
+ }
+
+ function renderRewards(rewards) {
+ var root = document.getElementById('rewardGroups');
+ var groups = normalizeRewards(rewards);
+ root.innerHTML = groups
+ .map(function (group) {
+ return [
+ '',
+ '',
+ '
',
+ 'TOP',
+ group.rank_no,
+ '',
+ '
',
+ group.items.map(rewardItem).join(''),
+ '',
+ ].join('');
+ })
+ .join('');
+ }
+
+ function normalizeRewards(rewards) {
+ var tops = [1488, 1985, 2482];
+ var input = rewards && rewards.length ? rewards : [];
+ return [1, 2, 3].map(function (rankNo, index) {
+ var reward =
+ input.find(function (item) {
+ return Number(item.rank_no || item.rankNo) === rankNo;
+ }) || {};
+ var items = reward.items || reward.resources || [];
+ if (!items.length) {
+ items = [
+ {
+ label: reward.name || 'Resource Group #' + (reward.resource_group_id || reward.resourceGroupId || '-'),
+ resource_group_id: reward.resource_group_id || reward.resourceGroupId,
+ image: reward.image_url || reward.imageUrl || reward.icon_url || reward.iconUrl,
+ single: true,
+ },
+ ];
+ }
+ return { rank_no: rankNo, top: tops[index], items: items.slice(0, 3) };
+ });
+ }
+
+ function renderGiftStrip(gifts) {
+ var cards = Array.prototype.slice.call(document.querySelectorAll('.gift-strip .gift-card'));
+ cards.forEach(function (card, index) {
+ var gift = (gifts || [])[index] || {};
+ var img = gift.image_url || gift.imageUrl || gift.icon_url || gift.iconUrl || gift.asset_url || gift.assetUrl;
+ var label = gift.name || gift.display_name || gift.displayName || gift.gift_id || gift.giftId || '';
+ var image = card.querySelector('.gift-img');
+ if (image && img) image.src = img;
+ var labelNode = card.querySelector('.gift-name');
+ if (!labelNode) {
+ labelNode = document.createElement('span');
+ labelNode.className = 'gift-name';
+ card.appendChild(labelNode);
+ }
+ labelNode.textContent = label;
+ });
+ }
+
+ function renderActivity() {
+ var cycle = state.current && state.current.cycle;
+ var historyCycle = state.historyCycles[0] || {};
+ var historyEntries = normalizeEntries(historyCycle.top_entries || historyCycle.topEntries || []);
+ var weeklyEntries = normalizeEntries(state.weeklyEntries);
+
+ if (cycle) {
+ dateText.textContent = formatRangeUTC(cycle.start_ms || cycle.startMs, cycle.end_ms || cycle.endMs);
+ note.innerHTML =
+ 'Note: Send the following three gifts to participate in the
ranking. Users who rank in the top 3 of the leaderboard
will receive rewards.';
+ renderGiftStrip(cycle.gifts || []);
+ renderRewards(cycle.rewards || []);
+ } else {
+ dateText.textContent = 'No active cycle';
+ note.textContent = state.error || 'No Weekly Star activity is configured for your region.';
+ renderGiftStrip([]);
+ renderRewards([]);
+ }
+
+ if (historyCycle.cycle) {
+ historyDate.textContent = formatDateRange(historyCycle.cycle.start_ms || historyCycle.cycle.startMs, historyCycle.cycle.end_ms || historyCycle.cycle.endMs);
+ } else {
+ historyDate.textContent = 'Previous Top 3 Records';
+ }
+
+ renderRankList('historyList', 1607, historyEntries, 'No previous ranking records.');
+ renderRankList('weeklyList', 1496, weeklyEntries, state.loading ? 'Loading...' : 'No ranking data yet.');
+ }
+
+ function normalizeEntries(entries) {
+ return (entries || []).map(function (entry, index) {
+ var user = entry.user || {};
+ return {
+ rank: Number(entry.rank_no || entry.rankNo || index + 1),
+ name: user.username || user.display_user_id || user.displayUserId || entry.user_id || entry.userId || '-',
+ score: entry.score || 0,
+ avatar: user.avatar || (Number(entry.rank_no || entry.rankNo) === 1 ? './assets/avatar-top.png' : './assets/avatar-user.png'),
+ };
+ });
+ }
+
+ function setTab(tabName) {
+ var next = tabName || 'history';
+ page.setAttribute('data-tab', next);
+ tabs.forEach(function (tab) {
+ var active = tab.getAttribute('data-tab-target') === next;
+ tab.classList.toggle('is-active', active);
+ tab.setAttribute('aria-selected', active ? 'true' : 'false');
+ });
+ panels.forEach(function (panel) {
+ panel.hidden = panel.getAttribute('data-panel') !== next;
+ });
+ stageWrap.style.height = next === 'rewards' ? '1090px' : '802px';
+ updateBodyHeight();
+ }
+
+ function normalizeTab(value) {
+ if (value === 'weekly' || value === 'rewards' || value === 'history') return value;
+ return 'history';
+ }
+
+ 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 loadMock() {
+ state.loading = false;
+ state.current = {
+ cycle: {
+ start_ms: Date.UTC(2025, 11, 28, 0, 0, 0),
+ end_ms: Date.UTC(2026, 0, 3, 23, 59, 0),
+ gifts: [{ gift_id: 'gift_1' }, { gift_id: 'gift_2' }, { gift_id: 'gift_3' }],
+ rewards: mockRewards,
+ },
+ };
+ state.weeklyEntries = mockRanks.map(function (item) {
+ return {
+ rank_no: item.rank,
+ score: item.score,
+ user: { username: item.name, avatar: item.avatar },
+ };
+ });
+ state.historyCycles = [
+ {
+ cycle: {
+ start_ms: Date.UTC(2025, 11, 7, 0, 0, 0),
+ end_ms: Date.UTC(2025, 11, 14, 23, 59, 0),
+ },
+ top_entries: state.weeklyEntries.slice(0, 3),
+ },
+ ];
+ renderActivity();
+ }
+
+ function loadReal() {
+ if (!window.HyAppAPI || !window.HyAppAPI.weeklyStar) {
+ state.loading = false;
+ state.error = 'API module is not available.';
+ renderActivity();
+ return;
+ }
+ renderRankList('weeklyList', 1496, [], 'Loading...');
+ window.HyAppAPI.weeklyStar
+ .current()
+ .then(function (current) {
+ state.current = current || {};
+ var cycle = state.current.cycle || {};
+ var cycleID = cycle.cycle_id || cycle.cycleId || '';
+ return Promise.all([
+ cycleID
+ ? window.HyAppAPI.weeklyStar.leaderboard({ cycle_id: cycleID, page_size: 20 })
+ : Promise.resolve({ entries: [] }),
+ window.HyAppAPI.weeklyStar.history(10),
+ ]);
+ })
+ .then(function (results) {
+ state.weeklyEntries = results[0].entries || [];
+ state.historyCycles = results[1].cycles || [];
+ state.error = '';
+ })
+ .catch(function (error) {
+ state.error = error.message || 'Unable to load Weekly Star.';
+ state.weeklyEntries = [];
+ state.historyCycles = [];
+ })
+ .finally(function () {
+ state.loading = false;
+ renderActivity();
+ });
+ }
+
+ function isMockMode() {
+ return new URLSearchParams(window.location.search).get('mock') === '1';
+ }
+
+ function formatRangeUTC(start, end) {
+ return formatDateTimeUTC(start) + '-' + formatDateTimeUTC(end) + '(UTC)';
+ }
+
+ function formatDateRange(start, end) {
+ return formatDateUTC(start).replace(/-/g, '/') + '-' + formatDateUTC(end).replace(/-/g, '/');
+ }
+
+ function formatDateTimeUTC(value) {
+ var date = new Date(Number(value || 0));
+ if (!Number(value || 0)) return '-';
+ return (
+ date.getUTCFullYear() +
+ '-' +
+ (date.getUTCMonth() + 1) +
+ '-' +
+ date.getUTCDate() +
+ ' ' +
+ pad(date.getUTCHours()) +
+ ':' +
+ pad(date.getUTCMinutes())
+ );
+ }
+
+ function formatDateUTC(value) {
+ var date = new Date(Number(value || 0));
+ if (!Number(value || 0)) return '-';
+ return date.getUTCFullYear() + '-' + pad(date.getUTCMonth() + 1) + '-' + pad(date.getUTCDate());
+ }
+
+ function formatNumber(value) {
+ return new Intl.NumberFormat('en-US').format(Number(value || 0));
+ }
+
+ function pad(value) {
+ return String(value).padStart(2, '0');
+ }
+
+ function escapeHTML(value) {
+ return String(value == null ? '' : value)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ }
+
+ function escapeAttr(value) {
+ return escapeHTML(value);
+ }
+
+ function init() {
+ tabs.forEach(function (tab) {
+ tab.addEventListener('click', function () {
+ var next = tab.getAttribute('data-tab-target');
+ setTab(next);
+ if (window.history && window.history.replaceState) {
+ window.history.replaceState(null, '', '#' + next);
+ }
+ });
+ });
+
+ window.addEventListener('resize', updateBodyHeight);
+ setTab(normalizeTab(window.location.hash.replace('#', '')));
+ if (isMockMode()) {
+ loadMock();
+ } else {
+ loadReal();
+ }
+ }
+
+ init();
+})();
diff --git a/activity/weekly-star/style.css b/activity/weekly-star/style.css
new file mode 100644
index 0000000..ee08e7e
--- /dev/null
+++ b/activity/weekly-star/style.css
@@ -0,0 +1,454 @@
+* {
+ 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: #050a36;
+ color: #f8e8ff;
+ font-family:
+ "Source Han Sans SC",
+ "Noto Sans CJK SC",
+ 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: 800px;
+ margin: 0 auto;
+ transform: scale(var(--app-scale));
+ transform-origin: top center;
+}
+
+.page {
+ position: relative;
+ width: 375px;
+ min-height: 800px;
+ overflow: hidden;
+ background: #050a36;
+}
+
+.stage-wrap {
+ position: relative;
+ width: 375px;
+ height: 802px;
+ overflow: hidden;
+}
+
+.page[data-tab="rewards"] .stage-wrap {
+ height: 1090px;
+}
+
+.stage {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 1080px;
+ height: 3138px;
+ overflow: hidden;
+ transform: scale(var(--design-scale));
+ transform-origin: top left;
+ background: #050a36;
+}
+
+.bg-stars,
+.hero-bg {
+ position: absolute;
+ pointer-events: none;
+ object-fit: cover;
+}
+
+.bg-stars {
+ left: -110px;
+ top: 0;
+ width: 1300px;
+ height: 2309px;
+ opacity: 0.06;
+}
+
+.hero-bg {
+ left: 0;
+ top: -24px;
+ width: 1080px;
+ height: 1919px;
+}
+
+.date-plate {
+ position: absolute;
+ left: 204px;
+ top: 726px;
+ width: 672px;
+ height: 95px;
+}
+
+.date-plate-img {
+ position: absolute;
+ inset: 0;
+ width: 672px;
+ height: 95px;
+ object-fit: fill;
+}
+
+.date-text {
+ position: absolute;
+ left: -6px;
+ top: 20px;
+ width: 684px;
+ color: #fff;
+ font-size: 30px;
+ font-weight: 600;
+ line-height: 42px;
+ text-align: center;
+ text-shadow:
+ 0 2px 2px rgba(31, 0, 72, 0.8),
+ 0 0 10px rgba(255, 120, 255, 0.55);
+ white-space: nowrap;
+}
+
+.note {
+ position: absolute;
+ left: 64px;
+ top: 861px;
+ width: 1016px;
+ margin: 0;
+ color: #f8e8ff;
+ font-size: 34px;
+ font-weight: 500;
+ line-height: 42px;
+ text-shadow: 0 2px 6px rgba(0, 0, 0, 0.36);
+}
+
+.gift-card {
+ position: absolute;
+ left: var(--x);
+ top: 1027px;
+ width: 257px;
+ height: 270px;
+}
+
+.gift-frame {
+ position: absolute;
+ inset: 0;
+ width: 257px;
+ height: 270px;
+ object-fit: fill;
+}
+
+.gift-glow {
+ position: absolute;
+ left: 19px;
+ top: 30px;
+ width: 219px;
+ height: 219px;
+ object-fit: contain;
+ opacity: 0.34;
+}
+
+.gift-img {
+ position: absolute;
+ left: 35px;
+ top: 41px;
+ width: 188px;
+ height: 188px;
+ object-fit: contain;
+}
+
+.gift-name {
+ position: absolute;
+ left: 18px;
+ top: 226px;
+ width: 221px;
+ overflow: hidden;
+ color: #fff;
+ font-size: 28px;
+ font-weight: 600;
+ line-height: 32px;
+ text-align: center;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ text-shadow: 0 2px 6px rgba(0, 0, 0, 0.55);
+}
+
+.tabs {
+ position: absolute;
+ left: 21px;
+ top: 1317px;
+ display: grid;
+ grid-template-columns: repeat(3, 335px);
+ column-gap: 18px;
+ width: 1041px;
+ height: 155px;
+}
+
+.tab {
+ position: relative;
+ width: 335px;
+ height: 155px;
+ color: rgba(255, 255, 255, 0.6);
+ font-size: 32px;
+ font-weight: 600;
+ line-height: 34px;
+ text-align: center;
+}
+
+.tab-img {
+ position: absolute;
+ inset: 0;
+ width: 335px;
+ height: 155px;
+ object-fit: fill;
+ pointer-events: none;
+}
+
+.tab .active-img {
+ display: none;
+}
+
+.tab.is-active {
+ color: #f8e8ff;
+}
+
+.tab.is-active .active-img {
+ display: block;
+}
+
+.tab.is-active .inactive-img {
+ display: none;
+}
+
+.tab span {
+ position: absolute;
+ left: 32px;
+ top: 56px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 270px;
+ height: 77px;
+ text-shadow: 0 2px 5px rgba(0, 0, 0, 0.42);
+}
+
+.tab-panel {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 1080px;
+}
+
+.history-date {
+ position: absolute;
+ left: 0;
+ top: 1514px;
+ width: 1080px;
+ margin: 0;
+ color: #ff77ef;
+ font-size: 46px;
+ font-weight: 500;
+ line-height: 46px;
+ text-align: center;
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
+}
+
+.rank-row {
+ position: absolute;
+ left: 0;
+ width: 1080px;
+ height: 140px;
+ color: #fff;
+}
+
+.rank-row.is-odd {
+ background: rgba(103, 0, 203, 0.2);
+}
+
+.rank-row.is-even {
+ background: #0a0d41;
+}
+
+.rank-medal {
+ position: absolute;
+ left: 45px;
+ top: 39px;
+ width: 69px;
+ height: 61px;
+ object-fit: contain;
+}
+
+.rank-number {
+ position: absolute;
+ left: 69px;
+ top: 49px;
+ width: 40px;
+ color: #fff;
+ font-size: 36px;
+ font-weight: 500;
+ line-height: 42px;
+ text-align: left;
+}
+
+.rank-avatar {
+ position: absolute;
+ left: 160px;
+ top: 13px;
+ width: 113px;
+ height: 113px;
+ border-radius: 57px;
+ object-fit: cover;
+}
+
+.rank-name,
+.rank-score {
+ position: absolute;
+ top: 49px;
+ overflow: hidden;
+ color: #fff;
+ font-size: 36px;
+ font-weight: 500;
+ line-height: 42px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.rank-name {
+ left: 313px;
+ width: 420px;
+}
+
+.rank-score {
+ left: 846px;
+ width: 190px;
+ text-align: left;
+}
+
+.rank-empty {
+ position: absolute;
+ left: 70px;
+ width: 940px;
+ color: rgba(255, 255, 255, 0.78);
+ font-size: 34px;
+ font-weight: 500;
+ line-height: 48px;
+ text-align: center;
+ text-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
+}
+
+.reward-frame-bg {
+ position: absolute;
+ left: 0;
+ top: 1489px;
+ width: 1080px;
+ height: 1601px;
+ object-fit: fill;
+ pointer-events: none;
+}
+
+.reward-group {
+ position: absolute;
+ left: 0;
+ width: 1080px;
+ height: 470px;
+}
+
+.reward-title {
+ position: absolute;
+ left: 189px;
+ top: 0;
+ width: 702px;
+ height: 98px;
+}
+
+.reward-title img {
+ position: absolute;
+ inset: 0;
+ width: 702px;
+ height: 98px;
+ object-fit: fill;
+}
+
+.reward-title span {
+ position: absolute;
+ left: 0;
+ top: 40px;
+ width: 702px;
+ color: #fff;
+ font-size: 40px;
+ font-weight: 500;
+ line-height: 40px;
+ text-align: center;
+ text-shadow:
+ 0 2px 4px rgba(0, 0, 0, 0.45),
+ 0 0 9px rgba(255, 93, 255, 0.55);
+}
+
+.reward-item {
+ position: absolute;
+ top: 128px;
+ width: 257px;
+ height: 316px;
+}
+
+.reward-item:nth-child(2) {
+ left: 90px;
+}
+
+.reward-item:nth-child(3) {
+ left: 412px;
+}
+
+.reward-item:nth-child(4) {
+ left: 733px;
+}
+
+.reward-item.is-single {
+ left: 412px;
+}
+
+.reward-item .gift-card {
+ left: 0;
+ top: 0;
+}
+
+.reward-label {
+ position: absolute;
+ left: -18px;
+ top: 290px;
+ width: 293px;
+ color: #fff;
+ font-size: 28px;
+ font-weight: 500;
+ line-height: 36px;
+ text-align: center;
+ white-space: nowrap;
+ text-shadow: 0 2px 6px rgba(0, 0, 0, 0.45);
+}
+
+@media (max-width: 374px) {
+ .app-viewport {
+ margin-left: 0;
+ margin-right: 0;
+ }
+}
diff --git a/common/api.js b/common/api.js
index 12804f1..3097a1b 100644
--- a/common/api.js
+++ b/common/api.js
@@ -458,6 +458,26 @@ var default_api = 'https://api.global-interaction.com/';
},
};
+ var weeklyStarAPI = {
+ current: function () {
+ return request('/api/v1/activities/weekly-star/current', {
+ method: 'GET',
+ });
+ },
+ leaderboard: function (params) {
+ return request('/api/v1/activities/weekly-star/leaderboard', {
+ method: 'GET',
+ query: params || {},
+ });
+ },
+ history: function (limit) {
+ return request('/api/v1/activities/weekly-star/history', {
+ method: 'GET',
+ query: { limit: limit || 10 },
+ });
+ },
+ };
+
window.HyAppAPI = {
baseURL: resolveBaseURL,
buildURL: buildURL,
@@ -492,6 +512,7 @@ var default_api = 'https://api.global-interaction.com/';
bdLeaderCenter: bdLeaderCenterAPI,
superadminCenter: superadminCenterAPI,
level: levelAPI,
+ weeklyStar: weeklyStarAPI,
};
clearLocalDevCache();
})();
diff --git a/common/i18n.js b/common/i18n.js
index 0e3f9c8..8387de4 100644
--- a/common/i18n.js
+++ b/common/i18n.js
@@ -10,6 +10,24 @@
var messages = {};
var currentLang = '';
+ function storageGet(key) {
+ try {
+ return window.localStorage ? window.localStorage.getItem(key) : '';
+ } catch (error) {
+ return '';
+ }
+ }
+
+ function storageSet(key, value) {
+ try {
+ if (window.localStorage) {
+ window.localStorage.setItem(key, value);
+ }
+ } catch (error) {
+ // Some in-app browser contexts disable localStorage. Language still works for the current page session.
+ }
+ }
+
function queryLang() {
return new URLSearchParams(window.location.search).get('lang') || '';
}
@@ -31,7 +49,7 @@
function resolveLang() {
return (
normalizeLang(queryLang()) ||
- normalizeLang(window.localStorage.getItem(STORAGE_KEY)) ||
+ normalizeLang(storageGet(STORAGE_KEY)) ||
normalizeLang(window.navigator.language) ||
DEFAULT_LANG
);
@@ -50,7 +68,7 @@
.then(function (json) {
messages = json || {};
currentLang = normalized;
- window.localStorage.setItem(STORAGE_KEY, currentLang);
+ storageSet(STORAGE_KEY, currentLang);
apply();
return messages;
})
diff --git a/common/locales/ar.json b/common/locales/ar.json
index a42600b..cdf894a 100644
--- a/common/locales/ar.json
+++ b/common/locales/ar.json
@@ -1,4 +1,64 @@
{
+ "roomReward.pageTitle": "فعالية مكافآت الغرفة",
+ "roomReward.pageLabel": "فعالية مكافآت الغرفة",
+ "roomReward.currentTarget": "هدف المكافأة الحالي",
+ "roomReward.tiersLabel": "مستويات مكافآت الغرفة",
+ "roomReward.progressLabel": "تقدم مكافآت غرفتي",
+ "roomReward.turnover": "دوران الغرفة",
+ "roomReward.receiveReward": "استلام مكافأة",
+ "roomReward.contribution": "مساهمة هذا الأسبوع:",
+ "roomReward.rewards": "مكافآت هذا الأسبوع:",
+ "roomReward.contributionReward": "مكافأة المساهمة",
+ "roomReward.weeklyRewards": "المكافآت الأسبوعية",
+ "roomReward.idPrefix": "المعرّف:",
+ "roomReward.roomOwner": "مالك الغرفة",
+ "roomReward.changeLanguage": "تغيير اللغة",
+ "rechargeReward.pageTitle": "مكافأة الشحن التراكمي",
+ "rechargeReward.pageLabel": "فعالية مكافأة الشحن التراكمي",
+ "rechargeReward.changeLanguage": "تغيير اللغة",
+ "rechargeReward.days": "أيام",
+ "rechargeReward.hours": "ساعات",
+ "rechargeReward.minutes": "دقائق",
+ "rechargeReward.seconds": "ثوانٍ",
+ "rechargeReward.idPrefix": "المعرّف:",
+ "rechargeReward.accumulatedRecharge": "إجمالي الشحن:",
+ "rechargeReward.rechargeTitle": "اشحن {amount}",
+ "invite.pageAria": "دعوة الأصدقاء",
+ "invite.heroAria": "ادع الأصدقاء واكسب المال",
+ "invite.countdownAria": "العد التنازلي",
+ "invite.changeLanguage": "تغيير اللغة",
+ "invite.days": "أيام",
+ "invite.hours": "ساعات",
+ "invite.minutes": "دقائق",
+ "invite.seconds": "ثوان",
+ "invite.yourReward": "مكافأتك",
+ "invite.rewardSubtitle": "(اكسب مقابل كل صديق تدعوه)",
+ "invite.invitationReceived": "تم استلام الدعوة",
+ "invite.topUpViaFriends": "الشحن عبر الأصدقاء",
+ "invite.startInviting": "ابدأ دعوة الأشخاص الآن",
+ "invite.copyInviteHint": "(انسخ رابط الدعوة وشاركه مع أصدقائك)",
+ "invite.copy": "نسخ",
+ "invite.totalInvitees": "إجمالي عدد المدعوين",
+ "invite.eligibleVoters": "عدد المؤهلين",
+ "invite.totalRecharged": "إجمالي شحن المدعوين",
+ "invite.myRewards": "مكافآتي",
+ "invite.monthlyTasks": "المهام الشهرية",
+ "invite.totalTransportation": "إجمالي النقل",
+ "invite.volume": "الحجم",
+ "invite.invitationQuota": "حصة الدعوة",
+ "invite.task": "المهمة",
+ "invite.milestoneTitle": "المرحلة 1: balabalabal",
+ "invite.totalSupply": "إجمالي المعروض: 49999 عملة",
+ "invite.invitePaidFriends": "ادع 10 أصدقاء دفعوا بالفعل",
+ "invite.claim": "استلام",
+ "invite.transportFootnote": "(تُمنح مكافآت أعلى مستوى فقط)",
+ "invite.quotaFootnote": "(ستتلقى مكافأة عند الوصول إلى كل مستوى)",
+ "invite.rank": "الترتيب",
+ "invite.peopleSuffix": "{value} أشخاص",
+ "invite.copied": "تم النسخ",
+ "invite.copyFailed": "فشل النسخ",
+ "invite.notAvailable": "غير متاح الآن",
+ "invite.claimed": "تم الاستلام",
"host_center.title": "مركز المضيف",
"host_center.back": "رجوع",
"host_center.change_language": "تغيير اللغة",
diff --git a/common/locales/en.json b/common/locales/en.json
index 484a856..32c7f5e 100644
--- a/common/locales/en.json
+++ b/common/locales/en.json
@@ -1,4 +1,64 @@
{
+ "roomReward.pageTitle": "Room Reward Event",
+ "roomReward.pageLabel": "Room Reward Event",
+ "roomReward.currentTarget": "Current reward target",
+ "roomReward.tiersLabel": "Room reward tiers",
+ "roomReward.progressLabel": "My room reward progress",
+ "roomReward.turnover": "Room turnover",
+ "roomReward.receiveReward": "Receive a reward",
+ "roomReward.contribution": "This week's contribution:",
+ "roomReward.rewards": "This week's rewards:",
+ "roomReward.contributionReward": "Contribution reward",
+ "roomReward.weeklyRewards": "Weekly rewards",
+ "roomReward.idPrefix": "ID:",
+ "roomReward.roomOwner": "Room owner",
+ "roomReward.changeLanguage": "Change language",
+ "rechargeReward.pageTitle": "Recharge Reward",
+ "rechargeReward.pageLabel": "Recharge Reward activity",
+ "rechargeReward.changeLanguage": "Change language",
+ "rechargeReward.days": "Days",
+ "rechargeReward.hours": "Hours",
+ "rechargeReward.minutes": "Minutes",
+ "rechargeReward.seconds": "Seconds",
+ "rechargeReward.idPrefix": "ID:",
+ "rechargeReward.accumulatedRecharge": "Accumulated Recharge:",
+ "rechargeReward.rechargeTitle": "Recharge {amount}",
+ "invite.pageAria": "Invite friends",
+ "invite.heroAria": "Invite friends make money",
+ "invite.countdownAria": "Countdown",
+ "invite.changeLanguage": "Change language",
+ "invite.days": "Days",
+ "invite.hours": "Hours",
+ "invite.minutes": "Minutes",
+ "invite.seconds": "Seconds",
+ "invite.yourReward": "Your Reward",
+ "invite.rewardSubtitle": "(Earn for every friend you invite)",
+ "invite.invitationReceived": "Invitation received",
+ "invite.topUpViaFriends": "Top up via friends",
+ "invite.startInviting": "Start inviting people now",
+ "invite.copyInviteHint": "(Copy your invitation link and share it with your friends)",
+ "invite.copy": "Copy",
+ "invite.totalInvitees": "Total number of invitees",
+ "invite.eligibleVoters": "Number of eligible voters",
+ "invite.totalRecharged": "Total amount recharged by invitees",
+ "invite.myRewards": "My Rewards",
+ "invite.monthlyTasks": "Monthly Tasks",
+ "invite.totalTransportation": "Total Transportation",
+ "invite.volume": "Volume",
+ "invite.invitationQuota": "Invitation Quota",
+ "invite.task": "Task",
+ "invite.milestoneTitle": "Milestone1:balabalabal",
+ "invite.totalSupply": "Total supply: 49999 coins",
+ "invite.invitePaidFriends": "Invite 10 friends who have already made a payment",
+ "invite.claim": "Claim",
+ "invite.transportFootnote": "(Only the highest-level bonuses may be awarded)",
+ "invite.quotaFootnote": "(You'll receive a reward upon reaching each level)",
+ "invite.rank": "Rank",
+ "invite.peopleSuffix": "{value} people",
+ "invite.copied": "Copied",
+ "invite.copyFailed": "Copy failed",
+ "invite.notAvailable": "Not available yet",
+ "invite.claimed": "Claimed",
"host_center.title": "Host Center",
"host_center.back": "Back",
"host_center.change_language": "Change language",
diff --git a/common/locales/es.json b/common/locales/es.json
index cd23bcc..2dc408a 100644
--- a/common/locales/es.json
+++ b/common/locales/es.json
@@ -1,4 +1,64 @@
{
+ "roomReward.pageTitle": "Evento de recompensas de sala",
+ "roomReward.pageLabel": "Evento de recompensas de sala",
+ "roomReward.currentTarget": "Objetivo de recompensa actual",
+ "roomReward.tiersLabel": "Niveles de recompensa de sala",
+ "roomReward.progressLabel": "Progreso de recompensas de mi sala",
+ "roomReward.turnover": "Facturación de la sala",
+ "roomReward.receiveReward": "Recibir recompensa",
+ "roomReward.contribution": "Contribución de esta semana:",
+ "roomReward.rewards": "Recompensas de esta semana:",
+ "roomReward.contributionReward": "Recompensa de contribución",
+ "roomReward.weeklyRewards": "Recompensas semanales",
+ "roomReward.idPrefix": "ID:",
+ "roomReward.roomOwner": "Anfitrión de la sala",
+ "roomReward.changeLanguage": "Cambiar idioma",
+ "rechargeReward.pageTitle": "Recompensa de recarga acumulada",
+ "rechargeReward.pageLabel": "Actividad de recompensa de recarga acumulada",
+ "rechargeReward.changeLanguage": "Cambiar idioma",
+ "rechargeReward.days": "Días",
+ "rechargeReward.hours": "Horas",
+ "rechargeReward.minutes": "Minutos",
+ "rechargeReward.seconds": "Segundos",
+ "rechargeReward.idPrefix": "ID:",
+ "rechargeReward.accumulatedRecharge": "Recarga acumulada:",
+ "rechargeReward.rechargeTitle": "Recarga {amount}",
+ "invite.pageAria": "Invitar amigos",
+ "invite.heroAria": "Invita amigos y gana dinero",
+ "invite.countdownAria": "Cuenta regresiva",
+ "invite.changeLanguage": "Cambiar idioma",
+ "invite.days": "Días",
+ "invite.hours": "Horas",
+ "invite.minutes": "Minutos",
+ "invite.seconds": "Segundos",
+ "invite.yourReward": "Tu recompensa",
+ "invite.rewardSubtitle": "(Gana por cada amigo que invites)",
+ "invite.invitationReceived": "Invitación recibida",
+ "invite.topUpViaFriends": "Recarga de amigos",
+ "invite.startInviting": "Empieza a invitar ahora",
+ "invite.copyInviteHint": "(Copia tu enlace de invitación y compártelo con tus amigos)",
+ "invite.copy": "Copiar",
+ "invite.totalInvitees": "Número total de invitados",
+ "invite.eligibleVoters": "Número de usuarios elegibles",
+ "invite.totalRecharged": "Total recargado por invitados",
+ "invite.myRewards": "Mis recompensas",
+ "invite.monthlyTasks": "Tareas mensuales",
+ "invite.totalTransportation": "Transporte total",
+ "invite.volume": "Volumen",
+ "invite.invitationQuota": "Cuota de invitación",
+ "invite.task": "Tarea",
+ "invite.milestoneTitle": "Hito1:balabalabal",
+ "invite.totalSupply": "Suministro total: 49999 monedas",
+ "invite.invitePaidFriends": "Invita a 10 amigos que ya hayan pagado",
+ "invite.claim": "Reclamar",
+ "invite.transportFootnote": "(Solo se otorgan los bonos del nivel más alto)",
+ "invite.quotaFootnote": "(Recibirás una recompensa al alcanzar cada nivel)",
+ "invite.rank": "Ranking",
+ "invite.peopleSuffix": "{value} personas",
+ "invite.copied": "Copiado",
+ "invite.copyFailed": "Error al copiar",
+ "invite.notAvailable": "Aún no disponible",
+ "invite.claimed": "Reclamado",
"host_center.title": "Centro de anfitrión",
"host_center.back": "Volver",
"host_center.change_language": "Cambiar idioma",
diff --git a/common/locales/id.json b/common/locales/id.json
index e81e1b0..e0a51ff 100644
--- a/common/locales/id.json
+++ b/common/locales/id.json
@@ -1,4 +1,64 @@
{
+ "roomReward.pageTitle": "Event Hadiah Room",
+ "roomReward.pageLabel": "Event Hadiah Room",
+ "roomReward.currentTarget": "Target hadiah saat ini",
+ "roomReward.tiersLabel": "Level hadiah room",
+ "roomReward.progressLabel": "Progress hadiah room saya",
+ "roomReward.turnover": "Omzet room",
+ "roomReward.receiveReward": "Terima hadiah",
+ "roomReward.contribution": "Kontribusi minggu ini:",
+ "roomReward.rewards": "Hadiah minggu ini:",
+ "roomReward.contributionReward": "Hadiah kontribusi",
+ "roomReward.weeklyRewards": "Hadiah mingguan",
+ "roomReward.idPrefix": "ID:",
+ "roomReward.roomOwner": "Pemilik room",
+ "roomReward.changeLanguage": "Ganti bahasa",
+ "rechargeReward.pageTitle": "Hadiah Recharge Akumulatif",
+ "rechargeReward.pageLabel": "Event Hadiah Recharge Akumulatif",
+ "rechargeReward.changeLanguage": "Ganti bahasa",
+ "rechargeReward.days": "Hari",
+ "rechargeReward.hours": "Jam",
+ "rechargeReward.minutes": "Menit",
+ "rechargeReward.seconds": "Detik",
+ "rechargeReward.idPrefix": "ID:",
+ "rechargeReward.accumulatedRecharge": "Recharge akumulatif:",
+ "rechargeReward.rechargeTitle": "Recharge {amount}",
+ "invite.pageAria": "Undang teman",
+ "invite.heroAria": "Undang teman dan hasilkan uang",
+ "invite.countdownAria": "Hitung mundur",
+ "invite.changeLanguage": "Ganti bahasa",
+ "invite.days": "Hari",
+ "invite.hours": "Jam",
+ "invite.minutes": "Menit",
+ "invite.seconds": "Detik",
+ "invite.yourReward": "Hadiahmu",
+ "invite.rewardSubtitle": "(Dapatkan untuk setiap teman yang kamu undang)",
+ "invite.invitationReceived": "Undangan diterima",
+ "invite.topUpViaFriends": "Top up lewat teman",
+ "invite.startInviting": "Mulai undang orang sekarang",
+ "invite.copyInviteHint": "(Salin tautan undanganmu dan bagikan ke teman)",
+ "invite.copy": "Salin",
+ "invite.totalInvitees": "Jumlah total undangan",
+ "invite.eligibleVoters": "Jumlah pengguna yang memenuhi syarat",
+ "invite.totalRecharged": "Total top up oleh undangan",
+ "invite.myRewards": "Hadiah saya",
+ "invite.monthlyTasks": "Tugas Bulanan",
+ "invite.totalTransportation": "Total Transportasi",
+ "invite.volume": "Volume",
+ "invite.invitationQuota": "Kuota Undangan",
+ "invite.task": "Tugas",
+ "invite.milestoneTitle": "Milestone1:balabalabal",
+ "invite.totalSupply": "Total suplai: 49999 koin",
+ "invite.invitePaidFriends": "Undang 10 teman yang sudah melakukan pembayaran",
+ "invite.claim": "Klaim",
+ "invite.transportFootnote": "(Hanya bonus level tertinggi yang diberikan)",
+ "invite.quotaFootnote": "(Kamu akan menerima hadiah saat mencapai setiap level)",
+ "invite.rank": "Peringkat",
+ "invite.peopleSuffix": "{value} orang",
+ "invite.copied": "Disalin",
+ "invite.copyFailed": "Gagal menyalin",
+ "invite.notAvailable": "Belum tersedia",
+ "invite.claimed": "Diklaim",
"host_center.title": "Pusat Host",
"host_center.back": "Kembali",
"host_center.change_language": "Ganti bahasa",
diff --git a/common/locales/tr.json b/common/locales/tr.json
index 7d25f08..d9afe8e 100644
--- a/common/locales/tr.json
+++ b/common/locales/tr.json
@@ -1,4 +1,64 @@
{
+ "roomReward.pageTitle": "Oda Ödül Etkinliği",
+ "roomReward.pageLabel": "Oda Ödül Etkinliği",
+ "roomReward.currentTarget": "Mevcut ödül hedefi",
+ "roomReward.tiersLabel": "Oda ödül kademeleri",
+ "roomReward.progressLabel": "Oda ödül ilerlemem",
+ "roomReward.turnover": "Oda cirosu",
+ "roomReward.receiveReward": "Ödül al",
+ "roomReward.contribution": "Bu haftaki katkı:",
+ "roomReward.rewards": "Bu haftaki ödüller:",
+ "roomReward.contributionReward": "Katkı ödülü",
+ "roomReward.weeklyRewards": "Haftalık ödüller",
+ "roomReward.idPrefix": "ID:",
+ "roomReward.roomOwner": "Oda sahibi",
+ "roomReward.changeLanguage": "Dili değiştir",
+ "rechargeReward.pageTitle": "Kümülatif Yükleme Ödülü",
+ "rechargeReward.pageLabel": "Kümülatif Yükleme Ödülü etkinliği",
+ "rechargeReward.changeLanguage": "Dili değiştir",
+ "rechargeReward.days": "Gün",
+ "rechargeReward.hours": "Saat",
+ "rechargeReward.minutes": "Dakika",
+ "rechargeReward.seconds": "Saniye",
+ "rechargeReward.idPrefix": "ID:",
+ "rechargeReward.accumulatedRecharge": "Toplam yükleme:",
+ "rechargeReward.rechargeTitle": "{amount} yükle",
+ "invite.pageAria": "Arkadaş davet et",
+ "invite.heroAria": "Arkadaş davet et, para kazan",
+ "invite.countdownAria": "Geri sayım",
+ "invite.changeLanguage": "Dili değiştir",
+ "invite.days": "Gün",
+ "invite.hours": "Saat",
+ "invite.minutes": "Dakika",
+ "invite.seconds": "Saniye",
+ "invite.yourReward": "Ödülün",
+ "invite.rewardSubtitle": "(Davet ettiğin her arkadaş için kazan)",
+ "invite.invitationReceived": "Davet alındı",
+ "invite.topUpViaFriends": "Arkadaşlarla yükleme",
+ "invite.startInviting": "Şimdi davet etmeye başla",
+ "invite.copyInviteHint": "(Davet bağlantını kopyala ve arkadaşlarınla paylaş)",
+ "invite.copy": "Kopyala",
+ "invite.totalInvitees": "Toplam davetli sayısı",
+ "invite.eligibleVoters": "Uygun kullanıcı sayısı",
+ "invite.totalRecharged": "Davetlilerin toplam yüklemesi",
+ "invite.myRewards": "Ödüllerim",
+ "invite.monthlyTasks": "Aylık Görevler",
+ "invite.totalTransportation": "Toplam Taşıma",
+ "invite.volume": "Hacim",
+ "invite.invitationQuota": "Davet Kotası",
+ "invite.task": "Görev",
+ "invite.milestoneTitle": "Kilometre taşı1:balabalabal",
+ "invite.totalSupply": "Toplam arz: 49999 coin",
+ "invite.invitePaidFriends": "Ödeme yapmış 10 arkadaş davet et",
+ "invite.claim": "Al",
+ "invite.transportFootnote": "(Yalnızca en yüksek seviye bonusları verilir)",
+ "invite.quotaFootnote": "(Her seviyeye ulaştığında ödül alırsın)",
+ "invite.rank": "Sıralama",
+ "invite.peopleSuffix": "{value} kişi",
+ "invite.copied": "Kopyalandı",
+ "invite.copyFailed": "Kopyalama başarısız",
+ "invite.notAvailable": "Henüz kullanılamaz",
+ "invite.claimed": "Alındı",
"host_center.title": "Host Merkezi",
"host_center.back": "Geri",
"host_center.change_language": "Dili değiştir",
diff --git a/common/locales/zh.json b/common/locales/zh.json
index b3af8ee..c2713b6 100644
--- a/common/locales/zh.json
+++ b/common/locales/zh.json
@@ -1,4 +1,64 @@
{
+ "roomReward.pageTitle": "房间奖励活动",
+ "roomReward.pageLabel": "房间奖励活动",
+ "roomReward.currentTarget": "当前奖励目标",
+ "roomReward.tiersLabel": "房间奖励档位",
+ "roomReward.progressLabel": "我的房间奖励进度",
+ "roomReward.turnover": "房间流水",
+ "roomReward.receiveReward": "获得奖励",
+ "roomReward.contribution": "本周贡献:",
+ "roomReward.rewards": "本周奖励:",
+ "roomReward.contributionReward": "贡献奖励",
+ "roomReward.weeklyRewards": "每周奖励",
+ "roomReward.idPrefix": "ID:",
+ "roomReward.roomOwner": "房主",
+ "roomReward.changeLanguage": "切换语言",
+ "rechargeReward.pageTitle": "累充奖励",
+ "rechargeReward.pageLabel": "累充奖励活动",
+ "rechargeReward.changeLanguage": "切换语言",
+ "rechargeReward.days": "天",
+ "rechargeReward.hours": "小时",
+ "rechargeReward.minutes": "分钟",
+ "rechargeReward.seconds": "秒",
+ "rechargeReward.idPrefix": "ID:",
+ "rechargeReward.accumulatedRecharge": "累计充值:",
+ "rechargeReward.rechargeTitle": "充值 {amount}",
+ "invite.pageAria": "邀请好友",
+ "invite.heroAria": "邀请好友赚钱",
+ "invite.countdownAria": "倒计时",
+ "invite.changeLanguage": "切换语言",
+ "invite.days": "天",
+ "invite.hours": "小时",
+ "invite.minutes": "分钟",
+ "invite.seconds": "秒",
+ "invite.yourReward": "你的奖励",
+ "invite.rewardSubtitle": "(每邀请一位好友都可获得奖励)",
+ "invite.invitationReceived": "收到邀请",
+ "invite.topUpViaFriends": "好友充值",
+ "invite.startInviting": "立即开始邀请好友",
+ "invite.copyInviteHint": "(复制你的邀请链接并分享给好友)",
+ "invite.copy": "复制",
+ "invite.totalInvitees": "邀请总人数",
+ "invite.eligibleVoters": "符合条件人数",
+ "invite.totalRecharged": "被邀请人充值总额",
+ "invite.myRewards": "我的奖励",
+ "invite.monthlyTasks": "月度任务",
+ "invite.totalTransportation": "总运输",
+ "invite.volume": "数量",
+ "invite.invitationQuota": "邀请额度",
+ "invite.task": "任务",
+ "invite.milestoneTitle": "里程碑1:balabalabal",
+ "invite.totalSupply": "总供应:49999 金币",
+ "invite.invitePaidFriends": "邀请 10 位已完成支付的好友",
+ "invite.claim": "领取",
+ "invite.transportFootnote": "(只发放最高档位奖励)",
+ "invite.quotaFootnote": "(每达到一个等级即可获得奖励)",
+ "invite.rank": "排行榜",
+ "invite.peopleSuffix": "{value}人",
+ "invite.copied": "已复制",
+ "invite.copyFailed": "复制失败",
+ "invite.notAvailable": "暂不可领取",
+ "invite.claimed": "已领取",
"host_center.title": "主播中心",
"host_center.back": "返回",
"host_center.change_language": "切换语言",