mirror of
https://ghproxy.com/https://github.com/StreakingMan/solvable-sheep-game
synced 2025-05-23 16:46:07 +08:00
283 lines
10 KiB
TypeScript
283 lines
10 KiB
TypeScript
import React, { FC, Suspense, useEffect, useRef, useState } from 'react';
|
||
import style from './Score.module.scss';
|
||
import Bmob from 'hydrogen-js-sdk';
|
||
import {
|
||
PLAYING_THEME_ID_STORAGE_KEY,
|
||
randomString,
|
||
timestampToUsedTimeString,
|
||
USER_ID_STORAGE_KEY,
|
||
USER_NAME_STORAGE_KEY,
|
||
} from '../utils';
|
||
import WxQrCode from './WxQrCode';
|
||
|
||
const Fireworks = React.lazy(() => import('./Fireworks'));
|
||
|
||
interface RankInfo {
|
||
// id
|
||
objectId?: string;
|
||
// 综合评分
|
||
rating: number;
|
||
// 通关数
|
||
level: number;
|
||
// 游戏得分
|
||
score: number;
|
||
// 主题id
|
||
themeId: string;
|
||
// 耗时
|
||
time: number;
|
||
// 用户昵称
|
||
username: string;
|
||
// 用户id
|
||
userId: string;
|
||
}
|
||
|
||
// 该组件条件渲染
|
||
const Score: FC<{
|
||
level: number;
|
||
score: number;
|
||
time: number;
|
||
success: boolean;
|
||
restartMethod: () => void;
|
||
}> = ({ level, score, time, success, restartMethod }) => {
|
||
const [rankList, setRankList] = useState<RankInfo[]>([]);
|
||
const [username, setUsername] = useState<string>(
|
||
localStorage.getItem(USER_NAME_STORAGE_KEY) || ''
|
||
);
|
||
const [userId, setUserId] = useState<string>(
|
||
localStorage.getItem(USER_ID_STORAGE_KEY) || ''
|
||
);
|
||
const usernameInputRef = useRef<HTMLInputElement>(null);
|
||
const [tip, setTip] = useState<string>('');
|
||
|
||
// 综合评分
|
||
const rating = Math.max(0, score) * 100 - Math.round(time / 1000);
|
||
// 分主题排行
|
||
const themeId = localStorage.getItem(PLAYING_THEME_ID_STORAGE_KEY);
|
||
|
||
const uploadRankInfo = (id?: string) => {
|
||
const _userId = localStorage.getItem(USER_ID_STORAGE_KEY);
|
||
const _username = localStorage.getItem(USER_NAME_STORAGE_KEY);
|
||
if (!themeId || !_userId || !_username) return;
|
||
const rankInfo: RankInfo = {
|
||
rating,
|
||
themeId,
|
||
level,
|
||
score,
|
||
time,
|
||
username: _username,
|
||
userId: _userId,
|
||
};
|
||
const query = Bmob.Query('rank');
|
||
id && query.set('id', id);
|
||
for (const [key, val] of Object.entries(rankInfo)) {
|
||
query.set(key, val);
|
||
}
|
||
query
|
||
.save()
|
||
.then(() => {
|
||
getRankList();
|
||
})
|
||
.catch((e) => {
|
||
console.log(e);
|
||
});
|
||
};
|
||
|
||
const getRankList = (cb?: (rankList: RankInfo[]) => void) => {
|
||
if (!themeId) return;
|
||
const query = Bmob.Query('rank');
|
||
query.equalTo('themeId', '==', themeId);
|
||
query.order('-rating');
|
||
query.limit(50);
|
||
query
|
||
.find()
|
||
.then((res) => {
|
||
setRankList(res as any);
|
||
cb && cb(res as any);
|
||
const _userId = localStorage.getItem(USER_ID_STORAGE_KEY);
|
||
if (_userId) {
|
||
setTimeout(() => {
|
||
const rankEl = document.getElementById(_userId + 'el');
|
||
rankEl?.scrollIntoView({ behavior: 'smooth' });
|
||
});
|
||
}
|
||
})
|
||
.catch((e) => {
|
||
console.log(e);
|
||
});
|
||
};
|
||
|
||
const onConfirmNameClick = () => {
|
||
const inputUsername = usernameInputRef.current?.value;
|
||
if (!inputUsername) return;
|
||
const newUserId = randomString(8);
|
||
setUsername(inputUsername);
|
||
setUserId(newUserId);
|
||
localStorage.setItem(USER_NAME_STORAGE_KEY, inputUsername);
|
||
localStorage.setItem(USER_ID_STORAGE_KEY, newUserId);
|
||
judgeAndUpload(rankList, newUserId);
|
||
};
|
||
|
||
// 判断是否需要上传记录
|
||
const judgeAndUpload = (_rankList: RankInfo[], _userId: string) => {
|
||
if (!_userId) return;
|
||
if (
|
||
_rankList.length < 50 ||
|
||
rating > _rankList[_rankList.length - 1].rating
|
||
) {
|
||
// 榜未满或者分数高于榜上最后一名
|
||
// 本次排名
|
||
let thisRank = _rankList.findIndex((rank) => rank.rating < rating);
|
||
if (thisRank === -1) {
|
||
thisRank = _rankList.length + 1;
|
||
} else {
|
||
thisRank++;
|
||
}
|
||
// 查找是否曾上榜
|
||
const findSelf = _rankList.findIndex(
|
||
(rank) => rank.userId === _userId
|
||
);
|
||
if (findSelf === -1) {
|
||
// 新上榜
|
||
uploadRankInfo();
|
||
setTip(`恭喜上榜!本次排名${thisRank}`);
|
||
} else {
|
||
if (_rankList[findSelf].rating < rating) {
|
||
// 破自己记录
|
||
uploadRankInfo(_rankList[findSelf].objectId);
|
||
setTip(`个人新高!本次排名${thisRank}`);
|
||
} else if (_rankList[findSelf].rating > rating) {
|
||
// 没破自己记录
|
||
setTip(
|
||
`距离你的最高记录${_rankList[findSelf].rating}还差一点~`
|
||
);
|
||
} else {
|
||
setTip(`与你的最高记录${_rankList[findSelf].rating}持平~`);
|
||
}
|
||
}
|
||
} else {
|
||
// 未上榜
|
||
setTip('本次未上榜');
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
if (!__DIY__) {
|
||
// 排行榜
|
||
getRankList((rankList) =>
|
||
judgeAndUpload(
|
||
rankList,
|
||
localStorage.getItem(USER_ID_STORAGE_KEY) || ''
|
||
)
|
||
);
|
||
}
|
||
}, []);
|
||
|
||
return (
|
||
<div className={style.modal}>
|
||
<Suspense fallback={<span>fireworks</span>}>
|
||
{success && <Fireworks />}
|
||
</Suspense>
|
||
<div className={style.inner}>
|
||
{success ? <h1>🎉恭喜通关!</h1> : <h1>😫就差一点!</h1>}
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>通关数</th>
|
||
<th>用时</th>
|
||
<th>得分</th>
|
||
<th>综合评分</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>{level}</td>
|
||
<td>{timestampToUsedTimeString(time)}</td>
|
||
<td>{score}</td>
|
||
<td>{rating}</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
{!__DIY__ && !username && (
|
||
<div className={'flex-container flex-center'}>
|
||
<input
|
||
className={style.nameInput}
|
||
ref={usernameInputRef}
|
||
placeholder={'留下大名进行排行榜pk!'}
|
||
/>
|
||
<button
|
||
className={'primary'}
|
||
onClick={onConfirmNameClick}
|
||
>
|
||
确定
|
||
</button>
|
||
</div>
|
||
)}
|
||
|
||
<div>{tip}</div>
|
||
|
||
{!__DIY__ && (
|
||
<div className={style.rankContainer}>
|
||
<h1 className={style.title}>TOP 50</h1>
|
||
{rankList.length ? (
|
||
<div className={style.list}>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>名次</th>
|
||
<th>名称</th>
|
||
<th>通关数</th>
|
||
{/*<th>用时</th>*/}
|
||
{/*<th>得分</th>*/}
|
||
<th>综合评分</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{rankList.map((rank, idx) => (
|
||
<tr
|
||
key={idx}
|
||
id={rank.userId}
|
||
style={{
|
||
background:
|
||
rank.userId === userId
|
||
? 'rgb(0 0 0 / 20%)'
|
||
: '',
|
||
}}
|
||
>
|
||
<td>{idx + 1}</td>
|
||
<td>
|
||
{rank.username}
|
||
{rank.userId === userId &&
|
||
'(你)'}
|
||
</td>
|
||
<td>{rank.level}</td>
|
||
{/*<td>*/}
|
||
{/* {timestampToUsedTimeString(*/}
|
||
{/* rank.time*/}
|
||
{/* )}*/}
|
||
{/*</td>*/}
|
||
{/*<td>{rank.score}</td>*/}
|
||
<td>{rank.rating}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
) : (
|
||
<div className={style.tip}>
|
||
暂无排行,速速霸榜!
|
||
</div>
|
||
)}
|
||
<button className={'primary'} onClick={restartMethod}>
|
||
再来一次
|
||
</button>
|
||
</div>
|
||
)}
|
||
<WxQrCode />
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default Score;
|