refactor: rewrite ranking page standalone
This commit is contained in:
parent
7b0a9e3224
commit
41b5e8e095
@ -22,12 +22,9 @@
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
window.__H5_ROUTE_PATH__ = "/ranking";
|
||||
</script>
|
||||
<script src="/assets/yumi-route-entry.js?v=20260424f"></script>
|
||||
<script type="module" crossorigin src="/js/index-CIAVq8iD-1776148658686.js?v=20260424e"></script>
|
||||
<link rel="stylesheet" crossorigin href="/css/index-VRlNQ74k-1776148661679.css">
|
||||
<link rel="stylesheet" href="/ranking/ranking.css?v=20260424h">
|
||||
<script defer src="/ranking/ranking.js?v=20260424h"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
507
h5/ranking/ranking.css
Normal file
507
h5/ranking/ranking.css
Normal file
@ -0,0 +1,507 @@
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
body {
|
||||
display: block;
|
||||
background: #000;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: block;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
body {
|
||||
display: block;
|
||||
place-items: initial;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: block;
|
||||
grid-template-columns: none;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ranking-page {
|
||||
width: 100vw;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ranking-page * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.bg-layer {
|
||||
width: 100vw;
|
||||
min-height: 100vh;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.bg-layer .bg-image {
|
||||
width: 100vw;
|
||||
object-fit: cover;
|
||||
object-position: center top;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #666;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ranking-loading-image {
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
display: block;
|
||||
width: 180px;
|
||||
max-width: 70vw;
|
||||
height: auto;
|
||||
margin: 0 auto 12px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.loading-container p {
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ranking-content {
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.ranking-content .bg-layer {
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.sticky-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
border-radius: 0 0 12px 12px;
|
||||
transition: all 0.3s;
|
||||
height: 35vw;
|
||||
}
|
||||
|
||||
.sticky-header.stuck {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
-webkit-backdrop-filter: blur(32px);
|
||||
backdrop-filter: blur(32px);
|
||||
}
|
||||
|
||||
.sticky-spacer {
|
||||
height: 13vw;
|
||||
}
|
||||
|
||||
.type-tabs-wrap {
|
||||
padding: 4px 8%;
|
||||
}
|
||||
|
||||
.type-tabs {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.type-tab {
|
||||
color: #fff;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
font: inherit;
|
||||
font-weight: 700;
|
||||
font-size: 1em;
|
||||
line-height: 1.4;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.type-tab.active {
|
||||
color: #ff9500;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
position: absolute;
|
||||
left: -4%;
|
||||
top: 45%;
|
||||
transform: translateY(-50%);
|
||||
width: 6%;
|
||||
min-width: 18px;
|
||||
display: block;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.back-btn img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.period-tabs {
|
||||
margin: 1vw 6vw 4vw;
|
||||
padding: 4px;
|
||||
border-radius: 32px;
|
||||
border: 1px solid #b2a67d;
|
||||
background: linear-gradient(90deg, #0d0d0d 0%, #232323 100%);
|
||||
-webkit-backdrop-filter: blur(32px);
|
||||
backdrop-filter: blur(32px);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.period-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.period-item.active {
|
||||
background-image: url("/assets/yumi-ranking/Ranking/Overall/tabSelectedBg.png");
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.period-tab {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
font: inherit;
|
||||
font-weight: 510;
|
||||
padding: 4px 10px;
|
||||
line-height: 1.4;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.period-item.active .period-tab {
|
||||
color: #000;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ranking-main {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.top-section {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.top-one-wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.top-pair-wrap {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.top-user {
|
||||
color: #000;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.top-user-inner {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.top-frame {
|
||||
display: block;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.top-avatar-layer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.top-avatar-box img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.top-user-info {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 1vw;
|
||||
}
|
||||
|
||||
.top-name-box {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.show-text {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.top1-text {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.top-text {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
.quantity-pill {
|
||||
position: relative;
|
||||
border-radius: 80px;
|
||||
border: 1px solid #ffdf36;
|
||||
background: linear-gradient(270deg, #000 0%, #3b3b3b 47.45%, #000 100%);
|
||||
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.4) inset;
|
||||
}
|
||||
|
||||
.quantity-inner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
|
||||
.coin-icon {
|
||||
display: block;
|
||||
width: 1.3em;
|
||||
aspect-ratio: 1 / 1;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.quantity-text {
|
||||
font-weight: 700;
|
||||
background: linear-gradient(180deg, #ffe656 0%, #fff 100%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.rank-list {
|
||||
padding: 3vw 3vw 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2vw;
|
||||
}
|
||||
|
||||
.item-center {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.item-center > .item-bg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.item-content::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rank-row {
|
||||
min-height: 19vw;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.rank-row .item-content {
|
||||
padding: 3vw;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.rank-left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.rank-number,
|
||||
.mine-rank {
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.rank-number div,
|
||||
.mine-rank div {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
display: block;
|
||||
width: 3em;
|
||||
aspect-ratio: 1 / 1;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.rank-name,
|
||||
.mine-name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 0 4px;
|
||||
font-weight: 700;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.rank-value,
|
||||
.mine-value {
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.rank-value-text,
|
||||
.mine-value-text {
|
||||
font-weight: 500;
|
||||
color: rgba(248, 182, 45, 1);
|
||||
}
|
||||
|
||||
.bottom-spacer {
|
||||
height: 26vw;
|
||||
}
|
||||
|
||||
.my-ranking {
|
||||
min-height: 25vw;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 999;
|
||||
padding: 0 1px;
|
||||
}
|
||||
|
||||
.my-ranking .item-content {
|
||||
padding: 4vw 4vw 0;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.mine-left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.load-more-sentinel {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.sr-status {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 360px) {
|
||||
.ranking-page,
|
||||
.top-user {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 360px) {
|
||||
.ranking-page {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.top-user {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.ranking-page,
|
||||
.top-user {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
.ranking-page,
|
||||
.top-user {
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
809
h5/ranking/ranking.js
Normal file
809
h5/ranking/ranking.js
Normal file
@ -0,0 +1,809 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var API_BASE_URL = "https://jvapi.haiyihy.com";
|
||||
var ASSET_BASE = "/assets/yumi-ranking/Ranking/Overall/";
|
||||
var BG_VERSION = "20260424c";
|
||||
var DEFAULT_AVATAR = "/assets/defaultAvatar-CdxWBK1k-1776148661448.png";
|
||||
var COIN_ICON = "/assets/coin-lz3Tp0nX-1776148661447.png";
|
||||
var LOADING_GIF = "/assets/loading-ranking.gif";
|
||||
var BACK_ICON =
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAACE4AAAhOAFFljFgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADxSURBVHgB7dvBDYJAFEXRHyuhBEuzA+3EWAmlUIJ2MDJCNATy5m1I1H9PwoZhww3Mgh8iAAAAAACllG48joGlOUxfPoZ6LvCOM5S1PrITcV4is1acuhY7O8SXmveX+gp14rJbZGQ8OdUlMiKOQByBOAJxBOIIxBGIIxBHII5AHIE4AnEE4gjEEYgjEKeBOMJ488d/irPHN+lHYz33JKIqywHflnNkZm7SRCJSA5EMRDIQyUAkA5EMRDIQyUAkA5EMRDIQyUAkA5EMRDIQyWBGOkVmRqR7ZNeKFJCRroHJRqS+8DvUWpkGk10AAAAAwO96AiOznp1YXpZWAAAAAElFTkSuQmCC";
|
||||
|
||||
var RANKING_TYPES = ["Wealth", "Charm", "Room"];
|
||||
var PERIODS = [
|
||||
{ key: "Daily", api: "DAY", label: "ranking_daily" },
|
||||
{ key: "Weekly", api: "WEEK", label: "ranking_weekly" },
|
||||
{ key: "Monthly", api: "MONTH", label: "ranking_monthly" }
|
||||
];
|
||||
var TYPE_LABELS = {
|
||||
Wealth: "ranking_wealth",
|
||||
Charm: "ranking_charm",
|
||||
Room: "ranking_room"
|
||||
};
|
||||
var TYPE_TO_MY_RANK = {
|
||||
Wealth: "GIFTS_SEND",
|
||||
Charm: "GIFTS_RECEIVED",
|
||||
Room: "ROOM_GIFTS"
|
||||
};
|
||||
var TYPE_ENDPOINTS = {
|
||||
Wealth: "/activity/leaderboard/gifts-send",
|
||||
Charm: "/activity/leaderboard/gifts-received",
|
||||
Room: "/activity/leaderboard/room-gifts"
|
||||
};
|
||||
var TYPE_ASSETS = {
|
||||
Wealth: {
|
||||
bg: "bgWealth",
|
||||
top1: "top1-wealth",
|
||||
top2: "top2",
|
||||
top3: "top3"
|
||||
},
|
||||
Charm: {
|
||||
bg: "bgCharm",
|
||||
top1: "top1-charm",
|
||||
top2: "top2",
|
||||
top3: "top3"
|
||||
},
|
||||
Room: {
|
||||
bg: "bgRoom",
|
||||
top1: "top1-room",
|
||||
top2: "top2-room",
|
||||
top3: "top3-room"
|
||||
}
|
||||
};
|
||||
var I18N = {
|
||||
en: {
|
||||
ranking_wealth: "Wealth",
|
||||
ranking_charm: "Charm",
|
||||
ranking_room: "Room",
|
||||
ranking_daily: "Daily",
|
||||
ranking_weekly: "Weekly",
|
||||
ranking_monthly: "Monthly",
|
||||
loading: "Loading"
|
||||
}
|
||||
};
|
||||
|
||||
var commonHeaders = {
|
||||
authorization: "Bearer undefined",
|
||||
"req-app-intel": "version=1.0.0;build=1;model=SM-G9550;sysVersion=9;channel=Google",
|
||||
"req-client": "Android",
|
||||
"req-imei": "8fa54d728ab449e04f9292329ed44bda",
|
||||
"req-lang": "id-ID",
|
||||
"req-sys-origin": "origin=ATYOU;originChild=ATYOU",
|
||||
"req-version": "V2",
|
||||
"req-zone": "Asia/Shanghai"
|
||||
};
|
||||
|
||||
var state = {
|
||||
rankingType: getInitialRankingType(),
|
||||
period: "Daily",
|
||||
dateType: "DAY",
|
||||
myRankType: "GIFTS_SEND",
|
||||
sourceRanking: {},
|
||||
visibleRanking: [],
|
||||
myRanking: {},
|
||||
visibleCount: 10,
|
||||
hasMore: true,
|
||||
loading: true,
|
||||
locale: getLocale(),
|
||||
isApp: isAppEnvironment(),
|
||||
requestId: 0
|
||||
};
|
||||
|
||||
var stickyObserver = null;
|
||||
var loadObserver = null;
|
||||
var lastTypeClick = 0;
|
||||
var lastPeriodClick = 0;
|
||||
|
||||
function asset(name) {
|
||||
return ASSET_BASE + name + ".png";
|
||||
}
|
||||
|
||||
function backgroundAsset(type) {
|
||||
return asset(TYPE_ASSETS[type].bg) + "?v=" + BG_VERSION;
|
||||
}
|
||||
|
||||
function t(key) {
|
||||
var messages = I18N[state.locale] || I18N.en;
|
||||
return messages[key] || I18N.en[key] || key;
|
||||
}
|
||||
|
||||
function getLocale() {
|
||||
var raw = getParam("lang") || navigator.language || "en";
|
||||
raw = String(raw).toLowerCase();
|
||||
if (raw.indexOf("ar") === 0) return "ar";
|
||||
if (raw.indexOf("tr") === 0) return "tr";
|
||||
if (raw.indexOf("bn") === 0) return "bn";
|
||||
return "en";
|
||||
}
|
||||
|
||||
function getInitialRankingType() {
|
||||
var first = getParam("first") || "Wealth";
|
||||
return RANKING_TYPES.indexOf(first) >= 0 ? first : "Wealth";
|
||||
}
|
||||
|
||||
function getParam(name) {
|
||||
var hashQuery = "";
|
||||
if (window.location.hash && window.location.hash.indexOf("?") >= 0) {
|
||||
hashQuery = window.location.hash.slice(window.location.hash.indexOf("?") + 1);
|
||||
}
|
||||
var hashValue = hashQuery ? new URLSearchParams(hashQuery).get(name) : null;
|
||||
var searchValue = new URLSearchParams(window.location.search).get(name);
|
||||
return hashValue || searchValue;
|
||||
}
|
||||
|
||||
function setHeadersFromApp(info) {
|
||||
var next = {};
|
||||
if (info.authorization !== undefined) next.authorization = info.authorization;
|
||||
if (info.reqLang) next["req-lang"] = info.reqLang;
|
||||
if (info.appVersion || info.buildVersion || info.appChannel) {
|
||||
var appIntel = [];
|
||||
if (info.appVersion) appIntel.push("version=" + info.appVersion);
|
||||
if (info.buildVersion) appIntel.push("build=" + info.buildVersion);
|
||||
if (info.appChannel) appIntel.push("channel=" + info.appChannel);
|
||||
if (appIntel.length) next["req-app-intel"] = appIntel.join(";");
|
||||
}
|
||||
if (info.reqImei) next["req-imei"] = info.reqImei;
|
||||
if (info.sysOrigin) {
|
||||
var origin = ["origin=" + info.sysOrigin];
|
||||
if (info.sysOriginChild) origin.push("child=" + info.sysOriginChild);
|
||||
next["req-sys-origin"] = origin.join(";");
|
||||
}
|
||||
commonHeaders = Object.assign({}, commonHeaders, next);
|
||||
}
|
||||
|
||||
function isMobileDevice() {
|
||||
return /Android|iPad|iPhone|iPod/.test(navigator.userAgent);
|
||||
}
|
||||
|
||||
function isAppEnvironment() {
|
||||
return !!(window.app || window.webkit || window.FlutterPageControl);
|
||||
}
|
||||
|
||||
function defaultBrowserHeaders() {
|
||||
var lang = navigator.language || "";
|
||||
return {
|
||||
authorization: "",
|
||||
reqLang: lang.toLowerCase().indexOf("ar") === 0
|
||||
? "ar"
|
||||
: lang.toLowerCase().indexOf("tr") === 0
|
||||
? "tr"
|
||||
: lang.toLowerCase().indexOf("bn") === 0
|
||||
? "bn"
|
||||
: "en",
|
||||
appVersion: "1.0.0",
|
||||
buildVersion: "1",
|
||||
appChannel: "Web",
|
||||
reqImei: "H5-STATIC",
|
||||
sysOrigin: "LIKEI",
|
||||
sysOriginChild: "LIKEI"
|
||||
};
|
||||
}
|
||||
|
||||
function mockAppAccessOrigin() {
|
||||
return JSON.stringify({
|
||||
Authorization: "Bearer FLUTTER_MOCK_TOKEN",
|
||||
"Req-Lang": "en",
|
||||
"Req-App-Intel": "build=1.0;version=2.1;channel=flutter;Req-Imei=flutter-device",
|
||||
"Req-Sys-Origin": "origin=ATYOU;originChild=ATYOU"
|
||||
});
|
||||
}
|
||||
|
||||
function parseKeyValueList(value) {
|
||||
var result = {};
|
||||
if (!value) return result;
|
||||
String(value).split(";").forEach(function (part) {
|
||||
if (!part || part.indexOf("=") < 0) return;
|
||||
var index = part.indexOf("=");
|
||||
result[part.slice(0, index)] = part.slice(index + 1);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function normalizeAccessOrigin(data) {
|
||||
if (!data) return {};
|
||||
var appIntel = parseKeyValueList(data["Req-App-Intel"] || "");
|
||||
var sysOrigin = parseKeyValueList(data["Req-Sys-Origin"] || "");
|
||||
return {
|
||||
reqLang: data["Req-Lang"],
|
||||
authorization: data.Authorization,
|
||||
buildVersion: appIntel.build,
|
||||
appVersion: appIntel.version,
|
||||
appChannel: appIntel.channel,
|
||||
reqImei: appIntel["Req-Imei"],
|
||||
sysOrigin: sysOrigin.origin,
|
||||
sysOriginChild: sysOrigin.child
|
||||
};
|
||||
}
|
||||
|
||||
function getAccessOriginFromApp() {
|
||||
return new Promise(function (resolve) {
|
||||
var resolved = false;
|
||||
var finish = function (value) {
|
||||
if (resolved) return;
|
||||
resolved = true;
|
||||
resolve(value);
|
||||
};
|
||||
window.renderData = finish;
|
||||
window.getIosAccessOriginParam = finish;
|
||||
|
||||
var attempts = 0;
|
||||
var check = function () {
|
||||
attempts += 1;
|
||||
if (window.app && typeof window.app.getAccessOrigin === "function") {
|
||||
try {
|
||||
finish(window.app.getAccessOrigin());
|
||||
} catch (error) {
|
||||
console.error("getAccessOrigin failed:", error);
|
||||
finish(mockAppAccessOrigin());
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (attempts < 50) {
|
||||
window.setTimeout(check, 100);
|
||||
return;
|
||||
}
|
||||
if (window.FlutterApp && window.FlutterApp.postMessage) {
|
||||
window.FlutterApp.postMessage("requestAccessOrigin");
|
||||
}
|
||||
finish(mockAppAccessOrigin());
|
||||
};
|
||||
|
||||
if (isMobileDevice()) {
|
||||
check();
|
||||
} else {
|
||||
finish(mockAppAccessOrigin());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function initializeAppConnection() {
|
||||
if (!isAppEnvironment()) {
|
||||
setHeadersFromApp(defaultBrowserHeaders());
|
||||
await fetchUserProfileAfterConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var raw = await getAccessOriginFromApp();
|
||||
var parsed = JSON.parse(raw);
|
||||
setHeadersFromApp(normalizeAccessOrigin(parsed));
|
||||
} catch (error) {
|
||||
console.error("Failed to parse access origin:", error);
|
||||
setHeadersFromApp(defaultBrowserHeaders());
|
||||
}
|
||||
await fetchUserProfileAfterConnection();
|
||||
}
|
||||
|
||||
async function fetchUserProfileAfterConnection() {
|
||||
try {
|
||||
var response = await apiGet("/team/member/profile");
|
||||
if (response && response.status && response.body) {
|
||||
localStorage.setItem("userProfile", JSON.stringify(response.body));
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Failed to fetch user info after connection:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function apiRequest(path, options) {
|
||||
var url = path.indexOf("http") === 0 ? path : API_BASE_URL + path;
|
||||
var init = Object.assign({ method: "GET" }, options || {});
|
||||
init.headers = Object.assign({}, commonHeaders, init.headers || {});
|
||||
if (init.body && typeof init.body !== "string" && !(init.body instanceof FormData)) {
|
||||
init.headers["Content-Type"] = "application/json";
|
||||
init.body = JSON.stringify(init.body);
|
||||
}
|
||||
|
||||
var response = await fetch(url, init);
|
||||
var data = await response.json();
|
||||
if (!response.ok) {
|
||||
throw makeApiError("HTTP Error: " + response.status + " " + response.statusText, response.status, data);
|
||||
}
|
||||
if (data && data.status === false) {
|
||||
throw makeApiError(data.errorMsg || data.message || "API Error: " + data.errorCode, response.status, data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function apiGet(path) {
|
||||
return apiRequest(path, { method: "GET" });
|
||||
}
|
||||
|
||||
function makeApiError(message, status, response) {
|
||||
var error = new Error(message);
|
||||
error.name = "ApiError";
|
||||
error.status = status || null;
|
||||
error.response = response || null;
|
||||
error.errorMsg = response && response.errorMsg;
|
||||
error.errorCode = response && response.errorCode;
|
||||
return error;
|
||||
}
|
||||
|
||||
async function fetchLeaderboard(type) {
|
||||
var response = await apiGet(TYPE_ENDPOINTS[type]);
|
||||
if (response && response.status && response.body) {
|
||||
state.sourceRanking = response.body;
|
||||
updatePeriodRanking();
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchMyRanking() {
|
||||
var path = "/activity/leaderboard/my-rank?type=" +
|
||||
encodeURIComponent(state.myRankType) +
|
||||
"&dateType=" +
|
||||
encodeURIComponent(state.dateType);
|
||||
var response = await apiGet(path);
|
||||
state.myRanking = response && response.status && response.body ? response.body : {};
|
||||
}
|
||||
|
||||
function updatePeriodRanking() {
|
||||
var body = state.sourceRanking || {};
|
||||
var rows = [];
|
||||
if (state.period === "Hourly" && body.hourly) rows = body.hourly;
|
||||
if (state.period === "Daily" && body.daily) rows = body.daily;
|
||||
if (state.period === "Weekly" && body.weekly) rows = body.weekly;
|
||||
if (state.period === "Monthly" && body.monthly) rows = body.monthly;
|
||||
state.visibleRanking = Array.isArray(rows) ? rows : [];
|
||||
state.visibleCount = 10;
|
||||
state.hasMore = true;
|
||||
}
|
||||
|
||||
async function preloadImages(urls) {
|
||||
await Promise.all(urls.map(function (url) {
|
||||
return new Promise(function (resolve) {
|
||||
var image = new Image();
|
||||
image.onload = resolve;
|
||||
image.onerror = resolve;
|
||||
image.src = url;
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
function preloadRankingAssets() {
|
||||
return preloadImages([
|
||||
backgroundAsset("Wealth"),
|
||||
backgroundAsset("Charm"),
|
||||
backgroundAsset("Room"),
|
||||
asset("top1-wealth"),
|
||||
asset("top1-charm"),
|
||||
asset("top1-room"),
|
||||
asset("top2"),
|
||||
asset("top3"),
|
||||
asset("top2-room"),
|
||||
asset("top3-room")
|
||||
]);
|
||||
}
|
||||
|
||||
async function loadInitialData() {
|
||||
applyTypeState(state.rankingType);
|
||||
render();
|
||||
try {
|
||||
await initializeAppConnection();
|
||||
await Promise.all([
|
||||
fetchCurrentRankingGroup(),
|
||||
preloadRankingAssets()
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error("Ranking preload failed:", error);
|
||||
} finally {
|
||||
state.loading = false;
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchCurrentRankingGroup() {
|
||||
var requestId = ++state.requestId;
|
||||
var type = state.rankingType;
|
||||
await Promise.all([
|
||||
fetchLeaderboard(type).catch(function (error) {
|
||||
console.error("Leaderboard request failed:", error);
|
||||
}),
|
||||
fetchMyRanking().catch(function (error) {
|
||||
console.error("My ranking request failed:", error);
|
||||
})
|
||||
]);
|
||||
if (requestId !== state.requestId) return;
|
||||
render();
|
||||
}
|
||||
|
||||
async function fetchOnlyMyRanking() {
|
||||
var requestId = ++state.requestId;
|
||||
try {
|
||||
await fetchMyRanking();
|
||||
} catch (error) {
|
||||
console.error("My ranking request failed:", error);
|
||||
}
|
||||
if (requestId !== state.requestId) return;
|
||||
render();
|
||||
}
|
||||
|
||||
function applyTypeState(type) {
|
||||
state.rankingType = type;
|
||||
state.myRankType = TYPE_TO_MY_RANK[type];
|
||||
}
|
||||
|
||||
function switchRankingType(type) {
|
||||
if (state.rankingType === type) return;
|
||||
var now = Date.now();
|
||||
if (now - lastTypeClick < 1000) return;
|
||||
lastTypeClick = now;
|
||||
|
||||
applyTypeState(type);
|
||||
state.period = "Daily";
|
||||
state.dateType = "DAY";
|
||||
state.sourceRanking = {};
|
||||
state.visibleRanking = [];
|
||||
state.myRanking = {};
|
||||
state.visibleCount = 10;
|
||||
state.hasMore = true;
|
||||
render();
|
||||
fetchCurrentRankingGroup();
|
||||
}
|
||||
|
||||
function switchPeriod(period) {
|
||||
if (state.period === period.key) return;
|
||||
var now = Date.now();
|
||||
if (now - lastPeriodClick < 500) return;
|
||||
lastPeriodClick = now;
|
||||
|
||||
state.period = period.key;
|
||||
state.dateType = period.api;
|
||||
state.visibleRanking = [];
|
||||
state.myRanking = {};
|
||||
updatePeriodRanking();
|
||||
render();
|
||||
fetchOnlyMyRanking();
|
||||
}
|
||||
|
||||
function loadMoreRows() {
|
||||
if (!state.hasMore) return;
|
||||
var total = state.visibleRanking.filter(function (_, index) {
|
||||
return index >= 3;
|
||||
}).length;
|
||||
var current = state.visibleCount;
|
||||
if (current < total) {
|
||||
state.visibleCount = Math.min(current + 10, total);
|
||||
if (state.visibleCount >= total) state.hasMore = false;
|
||||
render();
|
||||
} else {
|
||||
state.hasMore = false;
|
||||
}
|
||||
}
|
||||
|
||||
function getTopUsers() {
|
||||
var list = state.visibleRanking.slice();
|
||||
while (list.length < 3) {
|
||||
list.push({
|
||||
avatar: "",
|
||||
id: "",
|
||||
nickname: "",
|
||||
quantityFormat: ""
|
||||
});
|
||||
}
|
||||
return list.slice(0, 3);
|
||||
}
|
||||
|
||||
function getListRows() {
|
||||
return state.visibleRanking.filter(function (_, index) {
|
||||
return index >= 3;
|
||||
}).slice(0, state.visibleCount);
|
||||
}
|
||||
|
||||
function topLayout(type, isTopOne) {
|
||||
var result = {};
|
||||
if (type === "Wealth") {
|
||||
result.minHeight = isTopOne ? "76.5vw" : "44vw";
|
||||
result.avatarLayer = isTopOne ? "height:110%" : "height:72%";
|
||||
result.avatarWidth = isTopOne ? "31%" : "45%";
|
||||
result.nameBox = isTopOne ? "height:8vw;width:30%" : "height:5vw;width:50%";
|
||||
result.pillWidth = isTopOne ? "30%" : "50%";
|
||||
} else if (type === "Charm") {
|
||||
result.minHeight = isTopOne ? "80vw" : "44vw";
|
||||
result.avatarLayer = isTopOne ? "height:98%" : "height:74%";
|
||||
result.avatarWidth = isTopOne ? "30%" : "45%";
|
||||
result.nameBox = isTopOne ? "height:10vw;width:40%" : "height:5vw;width:40%";
|
||||
result.pillWidth = isTopOne ? "35%" : "50%";
|
||||
} else {
|
||||
result.minHeight = isTopOne ? "76vw" : "52vw";
|
||||
result.avatarLayer = isTopOne ? "height:110%" : "height:80%";
|
||||
result.avatarWidth = isTopOne ? "30%" : "50%";
|
||||
result.nameBox = isTopOne ? "height:7vw;width:28%" : "height:4.5vw;width:40%";
|
||||
result.pillWidth = isTopOne ? "30%" : "50%";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function render() {
|
||||
var app = document.getElementById("app");
|
||||
if (!app) return;
|
||||
if (state.loading) {
|
||||
app.innerHTML = renderLoading();
|
||||
return;
|
||||
}
|
||||
app.innerHTML = renderRanking();
|
||||
bindEvents();
|
||||
setupObservers();
|
||||
}
|
||||
|
||||
function renderBgLayer(zIndex) {
|
||||
var style = zIndex === undefined ? "" : ' style="z-index:' + zIndex + '"';
|
||||
return '<div class="bg-layer"' + style + '><img class="bg-image" src="' +
|
||||
escapeAttribute(backgroundAsset(state.rankingType)) +
|
||||
'" alt="background-0"></div>';
|
||||
}
|
||||
|
||||
function renderLoading() {
|
||||
return '<main class="ranking-page" data-ranking="' + state.rankingType + '">' +
|
||||
'<div class="loading-container">' +
|
||||
renderBgLayer() +
|
||||
'<img src="' + LOADING_GIF + '" alt="" class="ranking-loading-image">' +
|
||||
'<p>' + escapeHtml(t("loading")) + '...</p>' +
|
||||
'</div>' +
|
||||
'</main>';
|
||||
}
|
||||
|
||||
function renderRanking() {
|
||||
return '<main class="ranking-page" data-ranking="' + state.rankingType + '">' +
|
||||
'<div class="ranking-content">' +
|
||||
renderBgLayer(-1) +
|
||||
'<div class="sentinel"></div>' +
|
||||
renderHeader() +
|
||||
'<div class="ranking-main">' +
|
||||
renderTopSection() +
|
||||
renderRankList() +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="bottom-spacer"></div>' +
|
||||
renderMyRanking() +
|
||||
'<div class="sr-status" aria-live="polite"></div>' +
|
||||
'</main>';
|
||||
}
|
||||
|
||||
function renderHeader() {
|
||||
return '<div class="sticky-header">' +
|
||||
'<div class="sticky-spacer"></div>' +
|
||||
'<div class="type-tabs-wrap"><div class="type-tabs">' +
|
||||
RANKING_TYPES.map(function (type) {
|
||||
return '<button class="type-tab' + (state.rankingType === type ? " active" : "") +
|
||||
'" data-ranking-type="' + type + '">' +
|
||||
escapeHtml(t(TYPE_LABELS[type])) +
|
||||
'</button>';
|
||||
}).join("") +
|
||||
'<button class="back-btn" type="button" aria-label="back"><img src="' + BACK_ICON + '" alt=""></button>' +
|
||||
'</div></div>' +
|
||||
'<div class="period-tabs">' +
|
||||
PERIODS.map(function (period) {
|
||||
return '<div class="period-item' + (state.period === period.key ? " active" : "") + '">' +
|
||||
'<button class="period-tab" data-period="' + period.key + '">' +
|
||||
escapeHtml(t(period.label)) +
|
||||
'</button>' +
|
||||
'</div>';
|
||||
}).join("") +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function renderTopSection() {
|
||||
var users = getTopUsers();
|
||||
return '<div class="top-section">' +
|
||||
'<div class="top-one-wrap">' +
|
||||
renderTopUser(users[0], 0, true) +
|
||||
'</div>' +
|
||||
'<div class="top-pair-wrap">' +
|
||||
renderTopUser(users[1], 1, false) +
|
||||
renderTopUser(users[2], 2, false) +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function renderTopUser(user, index, isTopOne) {
|
||||
var assets = TYPE_ASSETS[state.rankingType];
|
||||
var frame = isTopOne ? assets.top1 : index === 1 ? assets.top2 : assets.top3;
|
||||
var layout = topLayout(state.rankingType, isTopOne);
|
||||
var width = isTopOne ? "85%" : "43%";
|
||||
var name = user && user.nickname ? user.nickname : "";
|
||||
var avatar = user && user.avatar ? user.avatar : "";
|
||||
var quantity = user && user.quantityFormat ? user.quantityFormat : "";
|
||||
var id = user && user.id ? user.id : "";
|
||||
return '<div class="top-user" style="width:' + width + '" data-user-id="' + escapeAttribute(id) + '">' +
|
||||
'<div class="top-user-inner" style="min-height:' + layout.minHeight + '">' +
|
||||
'<img class="top-frame" src="' + asset(frame) + '" alt="">' +
|
||||
'<div class="top-avatar-layer" style="' + layout.avatarLayer + '">' +
|
||||
'<div class="top-avatar-box" style="width:' + layout.avatarWidth + '">' +
|
||||
'<img src="' + escapeAttribute(avatar) + '" alt="" data-avatar>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="top-user-info">' +
|
||||
'<div class="top-name-box" style="' + layout.nameBox + '">' +
|
||||
'<div class="show-text ' + (isTopOne ? "top1-text" : "top-text") + '" style="font-weight:860">' + escapeHtml(name) + '</div>' +
|
||||
'</div>' +
|
||||
(quantity ? renderQuantityPill(quantity, layout.pillWidth, isTopOne) : "") +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function renderQuantityPill(quantity, width, isTopOne) {
|
||||
return '<div class="quantity-pill" style="width:' + width + '">' +
|
||||
'<div class="quantity-inner">' +
|
||||
'<img class="coin-icon" src="' + COIN_ICON + '" alt="">' +
|
||||
'<div class="quantity-text show-text ' + (isTopOne ? "top1-text" : "top-text") + '">' +
|
||||
escapeHtml(quantity) +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function renderRankList() {
|
||||
var rows = getListRows();
|
||||
return '<div class="rank-list">' +
|
||||
rows.map(function (row, index) {
|
||||
return renderRankRow(row, index + 4);
|
||||
}).join("") +
|
||||
(state.hasMore ? '<div class="load-more-sentinel"></div>' : "") +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function renderItemCenter(classes, minHeight, image, content, attrs) {
|
||||
return '<div class="item-center ' + classes + '"' + (attrs || "") + ' style="min-height:' + minHeight + '">' +
|
||||
'<img class="item-bg" src="' + image + '" alt="">' +
|
||||
'<div class="item-content">' + content + '</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function renderRankRow(row, rank) {
|
||||
var id = row && row.id ? row.id : "";
|
||||
var avatar = row && row.avatar ? row.avatar : "";
|
||||
var name = row && row.nickname ? row.nickname : "";
|
||||
var quantity = row && row.quantityFormat ? row.quantityFormat : "";
|
||||
var content = '<div class="rank-left">' +
|
||||
'<div class="rank-number"><div>' + rank + '</div></div>' +
|
||||
'<img class="avatar" src="' + escapeAttribute(avatar) + '" alt="" data-avatar>' +
|
||||
'<div class="rank-name">' + escapeHtml(name) + '</div>' +
|
||||
'</div>' +
|
||||
'<div class="rank-value">' +
|
||||
'<img class="coin-icon" src="' + COIN_ICON + '" alt="">' +
|
||||
'<div class="rank-value-text">' + escapeHtml(quantity) + '</div>' +
|
||||
'</div>';
|
||||
return renderItemCenter(
|
||||
"rank-row",
|
||||
"19vw",
|
||||
asset("RankingItem"),
|
||||
content,
|
||||
' data-user-id="' + escapeAttribute(id) + '"'
|
||||
);
|
||||
}
|
||||
|
||||
function renderMyRanking() {
|
||||
var mine = state.myRanking || {};
|
||||
var rank = mine.rank || 999;
|
||||
var avatar = mine.avatar || "";
|
||||
var name = mine.nickname || "";
|
||||
var quantity = mine.quantityFormat || 0;
|
||||
var content = '<div class="mine-left">' +
|
||||
'<div class="mine-rank"><div>' + escapeHtml(String(rank)) + '</div></div>' +
|
||||
'<img class="avatar" src="' + escapeAttribute(avatar) + '" alt="" data-avatar>' +
|
||||
'<div class="mine-name">' + escapeHtml(name) + '</div>' +
|
||||
'</div>' +
|
||||
'<div class="mine-value">' +
|
||||
'<img class="coin-icon" src="' + COIN_ICON + '" alt="">' +
|
||||
'<div class="mine-value-text">' + escapeHtml(String(quantity)) + '</div>' +
|
||||
'</div>';
|
||||
return renderItemCenter("my-ranking", "25vw", asset("myRankingBg"), content);
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
document.querySelectorAll("[data-ranking-type]").forEach(function (button) {
|
||||
button.addEventListener("click", function () {
|
||||
switchRankingType(button.getAttribute("data-ranking-type"));
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll("[data-period]").forEach(function (button) {
|
||||
button.addEventListener("click", function () {
|
||||
var key = button.getAttribute("data-period");
|
||||
var period = PERIODS.filter(function (item) {
|
||||
return item.key === key;
|
||||
})[0];
|
||||
if (period) switchPeriod(period);
|
||||
});
|
||||
});
|
||||
|
||||
var back = document.querySelector(".back-btn");
|
||||
if (back) back.addEventListener("click", closePage);
|
||||
|
||||
document.querySelectorAll("[data-user-id]").forEach(function (node) {
|
||||
node.addEventListener("click", function () {
|
||||
openAppTarget(node.getAttribute("data-user-id"));
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll("[data-avatar]").forEach(function (img) {
|
||||
img.addEventListener("error", function () {
|
||||
if (img.getAttribute("src") !== DEFAULT_AVATAR) {
|
||||
img.setAttribute("src", DEFAULT_AVATAR);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setupObservers() {
|
||||
if (stickyObserver) stickyObserver.disconnect();
|
||||
if (loadObserver) loadObserver.disconnect();
|
||||
|
||||
var sentinel = document.querySelector(".sentinel");
|
||||
var stickyHeader = document.querySelector(".sticky-header");
|
||||
if (sentinel && stickyHeader && "IntersectionObserver" in window) {
|
||||
stickyObserver = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (entry.isIntersecting) {
|
||||
stickyHeader.classList.remove("stuck");
|
||||
} else {
|
||||
stickyHeader.classList.add("stuck");
|
||||
}
|
||||
});
|
||||
});
|
||||
stickyObserver.observe(sentinel);
|
||||
}
|
||||
|
||||
var loadMore = document.querySelector(".load-more-sentinel");
|
||||
if (loadMore && "IntersectionObserver" in window) {
|
||||
loadObserver = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (entry.isIntersecting && state.hasMore) loadMoreRows();
|
||||
});
|
||||
}, { rootMargin: "100px" });
|
||||
loadObserver.observe(loadMore);
|
||||
}
|
||||
}
|
||||
|
||||
function closePage() {
|
||||
try {
|
||||
if (window.app && window.app.closePage) {
|
||||
window.app.closePage();
|
||||
} else if (window.FlutterPageControl && window.FlutterPageControl.postMessage) {
|
||||
window.FlutterPageControl.postMessage("close_page");
|
||||
} else {
|
||||
window.history.back();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("close page failed:", error);
|
||||
window.history.back();
|
||||
}
|
||||
}
|
||||
|
||||
function openAppTarget(id) {
|
||||
if (!id || !state.isApp) return;
|
||||
try {
|
||||
if (state.rankingType === "Room") {
|
||||
if (window.app && window.app.gotoRoom) {
|
||||
window.app.gotoRoom(id);
|
||||
} else if (window.FlutterPageControl && window.FlutterPageControl.postMessage) {
|
||||
window.FlutterPageControl.postMessage("go_to_room:" + id);
|
||||
}
|
||||
} else if (window.app && window.app.viewUserInfo) {
|
||||
window.app.viewUserInfo(id);
|
||||
} else if (window.FlutterPageControl && window.FlutterPageControl.postMessage) {
|
||||
window.FlutterPageControl.postMessage("view_user_info:" + id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("open app target failed:", error);
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value == null ? "" : value)
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
function escapeAttribute(value) {
|
||||
return escapeHtml(value);
|
||||
}
|
||||
|
||||
loadInitialData();
|
||||
})();
|
||||
Loading…
x
Reference in New Issue
Block a user