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