🎉 init:

This commit is contained in:
john 2024-07-20 10:32:01 +08:00
commit 46f7474f0b
76 changed files with 6104 additions and 0 deletions

11
.env.development Normal file
View File

@ -0,0 +1,11 @@
###
# @LastEditors: John
# @Date: 2024-06-18 10:12:21
# @LastEditTime: 2024-07-18 14:49:49
# @Author: John
###
VITE_BASE_URL=
VITE_BASE_API_URL=/dev
VITE_TG_COMMUNITY=johntest_bot
VITE_TG_BOT_NAME=johntest_bot
VITE_TG_BOT_WEBAPP_NAME=starcraft

11
.env.production Normal file
View File

@ -0,0 +1,11 @@
###
# @LastEditors: John
# @Date: 2024-06-26 15:04:10
# @LastEditTime: 2024-07-18 10:39:12
# @Author: John
###
VITE_BASE_URL=https://campaign.pineer.cc
VITE_BASE_API_URL=/api
VITE_TG_COMMUNITY=pioneer_community
VITE_TG_BOT_NAME=Pioneerer_bot
VITE_TG_BOT_WEBAPP_NAME=pioneer

18
.eslintrc.cjs Normal file
View File

@ -0,0 +1,18 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

BIN
.yarn/install-state.gz Normal file

Binary file not shown.

1
.yarnrc.yml Normal file
View File

@ -0,0 +1 @@
nodeLinker: node-modules

30
README.md Normal file
View File

@ -0,0 +1,30 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
- Configure the top-level `parserOptions` property like this:
```js
export default {
// other rules...
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
}
```
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list

8
iconfont.json Normal file
View File

@ -0,0 +1,8 @@
{
"symbol_url": "//at.alicdn.com/t/c/font_4623671_wsjnprwq51.js",
"use_typescript": true,
"save_dir": "./src/components/iconfont",
"trim_icon_prefix": "icon",
"unit": "px",
"default_icon_size": 18
}

27
index.html Normal file
View File

@ -0,0 +1,27 @@
<!--
* @LastEditors: John
* @Date: 2024-07-18 14:40:44
* @LastEditTime: 2024-07-18 16:19:18
* @Author: John
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta name="format-detection" content="telephone=no" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="MobileOptimized" content="176" />
<meta name="HandheldFriendly" content="True" />
<meta name="robots" content="noindex,nofollow" />
<title></title>
<script src="https://telegram.org/js/telegram-web-app.js?1"></script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

46
package.json Normal file
View File

@ -0,0 +1,46 @@
{
"name": "penguin_coop-tg-weapp",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"iconfont": "npx iconfont-h5"
},
"dependencies": {
"@hyper-fetch/core": "^5.7.5",
"@hyper-fetch/react": "^5.7.5",
"@vkruglikov/react-telegram-web-app": "2.1.9",
"antd-mobile": "^5.37.1",
"antd-mobile-icons": "^0.3.0",
"clsx": "^2.1.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.24.1",
"tailwind-merge": "^2.4.0",
"vconsole": "^3.15.1",
"zustand": "^4.5.4"
},
"devDependencies": {
"@types/node": "^20.14.10",
"@types/postcss-pxtorem": "^6.0.3",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.13.1",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.19",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.7",
"postcss": "^8.4.39",
"postcss-pxtorem": "^6.1.0",
"react-iconfont-cli": "^2.0.2",
"typescript": "^5.2.2",
"vite": "^5.3.1",
"vite-plugin-compression": "^0.5.1"
}
}

22
postcss.config.js Normal file
View File

@ -0,0 +1,22 @@
/*
* @LastEditors: John
* @Date: 2024-06-17 18:25:10
* @LastEditTime: 2024-07-13 14:06:25
* @Author: John
*/
export default {
plugins: {
autoprefixer: {},
"postcss-pxtorem": {
rootValue: 37.5,
propList: ["*"],
exclude: (e) => {
if (/.*-m\.css$/.test(e) || /.*-m.module\.css$/.test(e)) {
console.log(e);
return false;
}
return true;
},
},
},
};

1
public/pioneer.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1721123338744" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4804" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M0 512a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#25133A" p-id="4805"></path><path d="M240.919273 258.699636h436.084363a141.032727 141.032727 0 0 1 141.032728 141.032728v20.619636a246.597818 246.597818 0 0 1-246.597819 246.644364h-45.707636l-26.158545 98.304H385.489455l86.295272-343.04h115.432728l-36.305455 148.48q130.327273 14.987636 156.020364-115.432728c19.223273-101.515636-63.069091-96.162909-102.632728-96.162909h-230.865454q-106.868364 2.141091-132.514909-100.445091z" fill="#FCB44B" p-id="4806"></path><path d="M370.222545 564.363636h-115.432727l-49.152 200.936728h115.432727l49.152-200.936728z" fill="#FCB44B" p-id="4807"></path><path d="M407.645091 422.213818h-115.432727l-19.223273 79.127273h115.432727l19.223273-79.127273z" fill="#593B8B" p-id="4808"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

174
src/App.css Normal file
View File

@ -0,0 +1,174 @@
body,
html,
#root {
height: var(--tg-viewport-height);
background-repeat: no-repeat;
background-size: cover;
}
@font-face {
font-family: "Roboto";
src: url("./assets/font/Roboto-Medium.ttf") format("truetype");
}
@font-face {
font-family: "YouSheBiaoTiHei";
src: url("./assets/font/YouSheBiaoTiHei.ttf") format("truetype");
}
* {
-webkit-overflow-scrolling: touch;
}
html,
body,
h1,
h2,
h3,
h4,
h5,
h6,
div,
dl,
dt,
dd,
ul,
ol,
li,
p,
blockquote,
pre,
hr,
figure,
table,
caption,
th,
td,
form,
fieldset,
legend,
input,
button,
textarea,
menu {
margin: 0;
padding: 0;
}
header,
footer,
section,
article,
aside,
nav,
hgroup,
address,
figure,
figcaption,
menu,
details {
display: block;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
caption,
th {
text-align: left;
font-weight: normal;
}
html,
body,
fieldset,
img,
iframe,
abbr {
border: 0;
}
i,
cite,
em,
var,
address,
dfn {
font-style: normal;
}
[hidefocus],
summary {
outline: 0;
}
li {
list-style: none;
}
h1,
h2,
h3,
h4,
h5,
h6,
small {
font-size: 100%;
}
sup,
sub {
font-size: 83%;
}
pre,
code,
kbd,
samp {
font-family: inherit;
}
q:before,
q:after {
content: none;
}
textarea {
overflow: auto;
resize: none;
}
label,
summary {
cursor: default;
}
a,
button {
cursor: pointer;
}
h1,
h2,
h3,
h4,
h5,
h6,
em,
strong,
b {
font-weight: bold;
}
del,
ins,
u,
s,
a,
a:hover {
text-decoration: none;
}
body,
textarea,
input,
button,
select,
keygen,
legend {
font: 12px/1.14 Microsoft YaHei, arial, \5b8b\4f53;
color: #333;
outline: 0;
}
a,
a:hover {
color: #333;
}
* {
box-sizing: border-box;
}

71
src/App.tsx Normal file
View File

@ -0,0 +1,71 @@
/*
* @LastEditors: John
* @Date: 2024-07-13 10:21:58
* @LastEditTime: 2024-07-19 17:54:47
* @Author: John
*/
import { MemoryRouter, Route, Routes } from "react-router-dom";
import "./App.css";
import {
MainButton,
useInitData,
useWebApp,
} from "@vkruglikov/react-telegram-web-app";
import Home from "./pages/Home";
import Guide from "./pages/Guide";
import useUserStore from "./store/User";
import { useEffect } from "react";
import Index from "./pages/Index";
import {
api_get_user_information,
api_login,
api_query_whether_the_user_receives_the_registration_reward,
} from "./server/api";
function App() {
const WebApp = useWebApp();
const [initDataUnsafe, initData] = useInitData();
const { Token, UpdateToken, UpdateInvitationCode } = useUserStore();
console.log("WebApp:", WebApp);
console.log("initDataUnsafe:", initDataUnsafe);
console.log("initData:", initData);
console.log("window.location", window.location);
WebApp.setBackgroundColor("#000000");
WebApp.setHeaderColor("#000000");
// WebApp.MainButton.setParams({
// text: "NEXT",
// color: "#000000",
// is_visible: true,
// });
// WebApp.onEvent("mainButtonClicked", () => {
// WebApp.close();
// });
useEffect(() => {
// (async () => {
// if (initData && !Token) {
// setTimeout(async () => {
// }, 2000);
// }
// })();
return () => {};
}, [initData, Token]);
return (
<>
{!Token ? (
<MemoryRouter>
<Routes>
<Route path="/*" element={<Index />} />
</Routes>
</MemoryRouter>
) : (
<Guide />
)}
</>
);
}
export default App;

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="32" height="32" viewBox="0 0 32 32"><defs><clipPath id="master_svg0_1_0171"><rect x="28" y="4" width="24" height="24" rx="0"/></clipPath></defs><g><g><ellipse cx="16" cy="16" rx="16" ry="16" fill="#E2C72D" fill-opacity="1"/></g><g transform="matrix(-1,0,0,1,56,0)" clip-path="url(#master_svg0_1_0171)"><g><path d="M34,11C34,10.447715,34.447715,10,35,10C35,10,45,10,45,10C45.5523,10,46,10.447715,46,11C46,11.55228,45.5523,12,45,12C45,12,37.41421,12,37.41421,12C37.41421,12,45.7071,20.2929,45.7071,20.2929C46.0976,20.6834,46.0976,21.3166,45.7071,21.7071C45.3166,22.0976,44.6834,22.0976,44.2929,21.7071C44.2929,21.7071,36,13.41421,36,13.41421C36,13.41421,36,21,36,21C36,21.552300000000002,35.55228,22,35,22C34.447715,22,34,21.552300000000002,34,21C34,21,34,11,34,11C34,11,34,11,34,11Z" fill-rule="evenodd" fill="#000000" fill-opacity="1"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 970 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="345.0001220703125" height="48" viewBox="0 0 345.0001220703125 48"><g><path d="M0,34L13.6896,48L345,48L345,14L331.31,0L0,0L0,34Z" fill="#FFFFFF" fill-opacity="1"/></g></svg>

After

Width:  |  Height:  |  Size: 288 B

1
src/assets/buttom_bg.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="86" height="30" viewBox="0 0 86 30"><g><path d="M0,21.25L8.59347,30L86,30L86,8.75L77.4065,0L0,0L0,21.25Z" fill="#5858FF" fill-opacity="1"/></g></svg>

After

Width:  |  Height:  |  Size: 265 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="86" height="30" viewBox="0 0 86 30"><g><path d="M0,21.25L8.59347,30L86,30L86,8.75L77.4065,0L0,0L0,21.25Z" fill="#8D8B8B" fill-opacity="1"/></g></svg>

After

Width:  |  Height:  |  Size: 265 B

BIN
src/assets/dianbao.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

1
src/assets/dianbao.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="48" height="48" viewBox="0 0 48 48"><defs><clipPath id="master_svg0_1_1853"><rect x="8" y="8" width="32" height="32" rx="0"/></clipPath></defs><g><g><ellipse cx="24" cy="24" rx="24" ry="24" fill="#111B27" fill-opacity="1"/></g><g clip-path="url(#master_svg0_1_1853)"><g><path d="M21.02317,33.7862L21.40407,28.1873L31.6538,19.05047C32.103899999999996,18.63829,31.5499,18.43219,30.9612,18.81003L18.32223,26.7103L12.851109,24.99286C11.673779,24.649369999999998,11.673779,23.85935,13.12813,23.27541L34.3893,15.134681C35.3589,14.688144,36.293800000000005,15.375125,35.9129,16.85213L32.3117,33.7862C32.0693,34.9884,31.3421,35.2976,30.3033,34.7136L24.7976,30.6605L22.1312,33.236599999999996C21.8196,33.5458,21.57721,33.7862,21.02317,33.7862Z" fill="#FFFFFF" fill-opacity="1"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

Binary file not shown.

BIN
src/assets/guide_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

1
src/assets/guide_bg.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 667 KiB

BIN
src/assets/guide_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="42.0003662109375" height="42.00531005859375" viewBox="0 0 42.0003662109375 42.00531005859375"><g><g><g><path d="M0,22.28078828125L0,19.65228828125C0.11211,18.98068828125,0.229166,18.32068828125,0.337978,17.66068828125C2.16241,6.20493828125,12.9388,-1.58775171875,24.379,0.27589328125C35.5224,2.06452828125,43.3222,12.55878828125,41.8137,23.73608828125C40.6596,32.25358828125,34.3468,39.29588828125,26.0095,41.31388828125C24.7994,41.60758828125,23.5612,41.77758828125,22.3346,42.00528828125L19.7066,42.00528828125C19.212,41.92608828125,18.7009,41.84028828125,18.1981,41.77098828125C9.52769,40.47078828125,2.81429,34.39858828125,0.667713,25.87288828125C0.370952,24.69478828125,0.219274,23.47868828125,0,22.28078828125ZM17.6408,18.92788828125C18.9004,21.36338828125,20.0413,23.56618828125,21.2152,25.83828828125C17.6721,26.95528828125,14.3105,26.93388828125,11.206,24.51818828125C11.3143,24.95638828125,11.4743,25.38008828125,11.6825,25.78048828125C14.1028,30.30488828125,18.9944,32.40538828125,23.9998,31.06058828125C24.4944,30.92858828125,24.7944,30.99298828125,25.1011,31.41868828125C25.5237,32.02558828125,26.0071,32.58788828125,26.5437,33.09678828125C27.4488,33.92178828125,28.3572,34.77318828125,29.3975,35.40678828125C30.5335,36.09488828125,31.3397,35.64278828125,31.6183,34.34248828125C31.948,32.82778828125,31.493,31.38898828125,31.1237,29.94348828125C31.0511,30.50788828125,31.0957,31.07378828125,31.0396,31.62818828125C30.9605,32.43178828125,30.4395,32.74698828125,29.7982,32.27338828125C28.809,31.54408828125,27.9269,30.66458828125,26.9624,29.81478828125C27.246,29.56068828125,27.3416,29.46498828125,27.4471,29.38248828125C29.9202,27.48168828125,31.2952,24.97528828125,31.6892,21.88308828125C32.0321,19.20018828125,31.2638,17.60458828125,28.9392,16.66238828125C28.0949,16.34898828125,27.2194,16.12768828125,26.3277,16.00238828125C24.2471,15.64928828125,22.1846,15.91988828125,20.1369,16.30598828125C19.6077,16.40498828125,19.3241,16.27298828125,19.1593,15.75318828125C18.9527,15.07278828125,18.7038,14.40598828125,18.4141,13.75668828125C18.1255,13.13128828125,18.3333,12.82768828125,18.9087,12.55378828125C21.4344,11.34268828125,23.9998,11.23378828125,26.6228,12.18418828125L27.2031,12.40528828125C24.2932,10.23548828125,21.0882,9.80484828125,17.537,10.87238828125C17.5007,10.13818828125,17.4199,9.51938828125,17.4512,8.90722828125C17.4957,8.03270828125,18.0415,7.75219828125,18.7982,8.17955828125C19.1856,8.39736828125,19.5368,8.67456828125,19.9028,8.93527828125L20.0825,8.70097828125C19.1362,7.95020828125,18.2508,7.10043828125,17.227,6.47507828125C15.8273,5.61540828125,14.7688,6.23251828125,14.7672,7.86440828125C14.7935,9.03675828125,14.9114,10.20528828125,15.12,11.35918828125C15.1991,11.85418828125,15.1315,12.13798828125,14.7128,12.44818828125C12.8829,13.78158828125,11.5539,15.69168828125,10.939,17.87188828125C10.5828,19.09288828125,10.3504,20.34688828125,10.5565,21.60758828125L17.6408,18.92788828125Z" fill="#1B1464" fill-opacity="1"/></g><g><path d="M23.62051546875,24.82671978515625C22.50601546875,22.72787978515625,21.43602546875,20.71317978515625,20.21435546875,18.41798478515625C22.00811546875,18.36683398515625,23.54962546875,18.25298108515625,25.08454546875,18.30743258515625C26.00814546875,18.35940158515625,26.918605468750002,18.55076678515625,27.78507546875,18.87504478515625C28.89462546875,19.27765378515625,29.06938546875,20.03006978515625,28.39343546875,21.02008978515625C27.23606546875,22.71797978515625,25.55441546875,23.78224978515625,23.62051546875,24.82671978515625Z" fill="#1B1464" fill-opacity="1"/></g><g><path d="M16.157023515625,14.9727533125Q16.663173515625,16.3307353125,16.926953515625,17.0418953125L14.437457515625,18.1655753125L14.256103515625,18.0929653125L15.878403515625,14.8787013125L16.005343515625,14.6064453125L16.157023515625,14.9727533125Z" fill="#1B1464" fill-opacity="1"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

1
src/assets/home_bg.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 376 KiB

1
src/assets/kuang.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="215" height="157" viewBox="0 0 215 157"><g><g><path d="M0,0L0,15.5L1,15.5L1,1L15.5,1L15.5,0L0,0Z" fill-rule="evenodd" fill="#D8D8D8" fill-opacity="1"/></g><g transform="matrix(-1,0,0,1,429,0)"><path d="M214,0L214,15.5L215,15.5L215,1L229.5,1L229.5,0L214,0Z" fill-rule="evenodd" fill="#D8D8D8" fill-opacity="1"/></g><g transform="matrix(1,0,0,-1,0,313)"><path d="M0,156L0,171.5L1,171.5L1,157L15.5,157L15.5,156L0,156Z" fill-rule="evenodd" fill="#D8D8D8" fill-opacity="1"/></g><g transform="matrix(-1,0,0,-1,429,313)"><path d="M214,156L214,171.5L215,171.5L215,157L229.5,157L229.5,156L214,156Z" fill-rule="evenodd" fill="#D8D8D8" fill-opacity="1"/></g></g></svg>

After

Width:  |  Height:  |  Size: 773 B

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

1
src/assets/logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 93 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

1
src/assets/rank1.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="24" height="24" viewBox="0 0 24 24"><defs><clipPath id="master_svg0_2_1471"><rect x="0" y="0" width="24" height="24" rx="0"/></clipPath><linearGradient x1="0.03729605674743652" y1="0.31006065011024475" x2="0.9624395370483398" y2="0.6896783113479614" id="master_svg1_2_1551"><stop offset="0%" stop-color="#F7BD00" stop-opacity="1"/><stop offset="27.000001072883606%" stop-color="#EDAA00" stop-opacity="1"/><stop offset="64.99999761581421%" stop-color="#F9EF00" stop-opacity="1"/><stop offset="100%" stop-color="#F7BD00" stop-opacity="1"/></linearGradient><linearGradient x1="0.1464466154575348" y1="0.14638635516166687" x2="0.8535534143447876" y2="0.8534931540489197" id="master_svg2_2_1552"><stop offset="0%" stop-color="#F7BD00" stop-opacity="1"/><stop offset="34.99999940395355%" stop-color="#F9EF00" stop-opacity="1"/><stop offset="73.00000190734863%" stop-color="#EDAA00" stop-opacity="1"/><stop offset="100%" stop-color="#F7BD00" stop-opacity="1"/></linearGradient><linearGradient x1="0.5" y1="0.91044682264328" x2="0.5" y2="0.046372897922992706" id="master_svg3_2_1553"><stop offset="0%" stop-color="#F7BD00" stop-opacity="1"/><stop offset="34.99999940395355%" stop-color="#F9EF00" stop-opacity="1"/><stop offset="73.00000190734863%" stop-color="#EDAA00" stop-opacity="1"/><stop offset="100%" stop-color="#F7BD00" stop-opacity="1"/></linearGradient></defs><g clip-path="url(#master_svg0_2_1471)"><g><g><g><g transform="matrix(0.9251434803009033,-0.3796176314353943,0.3796176314353943,0.9251434803009033,-2.343110720385255,-0.98151359480363)"><ellipse cx="8.33968448638916" cy="17.45051670074463" rx="12" ry="12" fill="url(#master_svg1_2_1551)" fill-opacity="1"/></g><g transform="matrix(0.7071067690849304,-0.7071067690849304,0.7071067690849304,0.7071067690849304,-9.688210810436317,0.6105614204093399)"><ellipse cx="7.281386375427246" cy="23.38846492767334" rx="11.388479232788086" ry="11.388479232788086" fill="url(#master_svg2_2_1552)" fill-opacity="1"/></g><g transform="matrix(0.7071067690849304,-0.7071067690849304,0.7071067690849304,0.7071067690849304,-8.41073581768785,3.6946586932858736)"><ellipse cx="8.55876350402832" cy="20.304269790649414" rx="8.30428409576416" ry="8.30428409576416" fill="url(#master_svg3_2_1553)" fill-opacity="1"/></g><g style="mix-blend-mode:overlay"><path d="M4.11220047076416,12.407671876525878C4.10715047076416,9.103421876525879,6.065618470764161,6.111971876525879,9.096098470764161,4.795041876525879C12.12657847076416,3.4781088765258787,15.64981847076416,4.087429876525879,18.06201847076416,6.345631876525879C14.84731847076416,2.9934568765258787,9.505728470764161,2.937523876525879,6.22159847076416,6.221651876525879C2.9374704707641603,9.50578187652588,2.99340447076416,14.847371876525878,6.34557847076416,18.062071876525877C4.90858847076416,16.530171876525877,4.10985847076416,14.507971876525879,4.11220047076416,12.407671876525878Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:overlay"/></g><g style="opacity:0.5400000214576721;mix-blend-mode:multiply"><path d="M12.00013995513916,3.695725440979004L11.78743995513916,3.695725440979004C16.08378995513916,3.803684440979004,19.50098995513916,7.3347654409790035,19.46818995513916,11.632335440979004C19.43528995513916,15.929925440979003,15.964489955139161,19.408325440979006,11.66701995513916,19.450625440979003C7.369529955139161,19.492925440979004,3.83095995513916,16.083425440979006,3.71358203513916,11.787305440979004C3.70919245513916,11.858135440979003,3.70919245513916,11.929175440979003,3.71358203513916,12.000005440979004C3.71358161513916,16.586325440979003,7.43153995513916,20.304325440979003,12.01786995513916,20.304325440979003C16.604189955139162,20.304325440979003,20.32218995513916,16.586325440979003,20.32218995513916,12.000005440979004C20.32218995513916,7.413675440979004,16.604189955139162,3.695725440979004,12.01786995513916,3.695725440979004L12.00013995513916,3.695725440979004Z" fill="#AF4304" fill-opacity="1" style="mix-blend-mode:multiply"/></g><g><g><path d="M11.521418984375,15.952738550109864L11.521418984375,9.101928550109863L12.265878984375,9.855248550109863L9.970458984375,9.855248550109863L9.970458984375,8.463818550109863L13.214178984375,8.463818550109863L13.214178984375,15.952738550109864L11.521418984375,15.952738550109864Z" fill="#AF4304" fill-opacity="1"/></g></g></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

1
src/assets/rank2.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

1
src/assets/rank3.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

1
src/assets/react.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="346" height="779" viewBox="0 0 346 779"><g><g><g><path d="M0.5,778.5L12.8582,778.5L0.5,766.5L0.5,778.5Z" fill="#5858FF" fill-opacity="1"/><path d="M0,765.31756L0,778.5L0,779L14.0909,779L0,765.31756ZM1,778L11.6255,778L1,767.68244L1,778Z" fill-rule="evenodd" fill="#5858FF" fill-opacity="1"/></g><g transform="matrix(-1,-5.2146120310681e-8,5.2146120310681e-8,-1,690.9999993481736,25.000018016484567)"><path d="M345.5,24.5L357.8582,24.5L345.5,12.5L345.5,24.5Z" fill="#5858FF" fill-opacity="1"/><path d="M345,11.31756L345,24.5L345,25L359.0909,25L345,11.31756ZM346,24L356.6255,24L346,13.68244L346,24Z" fill-rule="evenodd" fill="#5858FF" fill-opacity="1"/></g></g><g><path d="M346,17.789L327.734,0L327.531,0L0,0L0,760.711L18.2655,778.5L346,778.5L346,17.789ZM327.328,1L1,1L1,760.289L18.672,777.5L345,777.5L345,18.211L327.328,1Z" fill-rule="evenodd" fill="#5858FF" fill-opacity="1"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1005 B

1
src/assets/tg_white.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="30" height="30" viewBox="0 0 30 30"><defs><clipPath id="master_svg0_5_4502"><rect x="6" y="6" width="18" height="18" rx="0"/></clipPath></defs><g><g><rect x="0" y="0" width="30" height="30" rx="15" fill="#FFFFFF" fill-opacity="1"/></g><g clip-path="url(#master_svg0_5_4502)"><g><path d="M13.32553,20.5048L13.53979,17.35537L19.3052,12.21589C19.558500000000002,11.98404,19.2468,11.86811,18.9157,12.080639999999999L11.80626,16.524549999999998L8.728749,15.558489999999999C8.0665,15.365269999999999,8.0665,14.92088,8.884572,14.59242L20.844,10.0132582C21.389400000000002,9.762081,21.915300000000002,10.148508,21.701,10.979330000000001L19.6753,20.5048C19.539,21.180999999999997,19.1299,21.3549,18.5456,21.026400000000002L15.44862,18.74651L13.948820000000001,20.1956C13.773520000000001,20.369500000000002,13.63718,20.5048,13.32553,20.5048Z" fill="#000000" fill-opacity="1"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 1000 B

BIN
src/assets/tuite.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

1
src/assets/tuite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="48" height="48" viewBox="0 0 48 48"><defs><clipPath id="master_svg0_1_1865"><rect x="8" y="8" width="32" height="32" rx="0"/></clipPath></defs><g><g><ellipse cx="24" cy="24" rx="24" ry="24" fill="#111B27" fill-opacity="1"/></g><g clip-path="url(#master_svg0_1_1865)"><g><path d="M14.0510992,14L22.15895,25.031100000000002L14,34L15.8364,34L22.97971,26.1475L28.7511,34L35,34L26.4358,22.348480000000002L34.0302,14L32.193799999999996,14L25.6154,21.23181L20.3,14L14.0510992,14ZM16.7516,15.376290000000001L19.622320000000002,15.376290000000001L32.299099999999996,32.6237L29.4284,32.6237L16.7516,15.376290000000001Z" fill="#FFFFFF" fill-opacity="1"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 777 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="30" height="30" viewBox="0 0 30 30"><g><g><rect x="0" y="0" width="30" height="30" rx="15" fill="#FFFFFF" fill-opacity="1"/></g><g><path d="M9.0316328,9L14.05078,15.61869L9,21L10.13682,21L14.558869999999999,16.28847L18.13163,21L22,21L16.69834,14.00909L21.3996,9L20.2628,9L16.19047,13.339089999999999L12.9,9L9.0316328,9ZM10.70337,9.825776L12.48048,9.825776L20.328,20.1742L18.55093,20.1742L10.70337,9.825776Z" fill="#000000" fill-opacity="1"/></g></g></svg>

After

Width:  |  Height:  |  Size: 571 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="345.0001220703125" height="68" viewBox="0 0 345.0001220703125 68"><g><path d="M0,54L13.6897,68L345,68L345,14L331.31,0L0,0L0,54Z" fill="#5858FF" fill-opacity="1"/></g></svg>

After

Width:  |  Height:  |  Size: 288 B

BIN
src/assets/youguang.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

1
src/assets/youguang.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="48" height="48" viewBox="0 0 48 48"><defs><clipPath id="master_svg0_1_1872"><rect x="8" y="8" width="32" height="32" rx="0"/></clipPath></defs><g><g><ellipse cx="24" cy="24" rx="24" ry="24" fill="#111B27" fill-opacity="1"/></g><g clip-path="url(#master_svg0_1_1872)"><g><path d="M37.4156,17.25312C37.0938,16.04688,36.143699999999995,15.096875,34.9406,14.775Q32.756299999999996,14.1875,24,14.1875Q15.24375,14.1875,13.05938,14.771875C11.85312,15.09375,10.90625,16.04375,10.584375,17.25Q10,19.4375,10,24Q10,28.5625,10.584375,30.7469C10.90625,31.9531,11.85625,32.903099999999995,13.05938,33.225Q15.24375,33.8125,24,33.8125Q32.756299999999996,33.8125,34.9406,33.225C36.1469,32.903099999999995,37.0938,31.9531,37.4156,30.7469Q38,28.5625,38,24Q38,19.4375,37.4156,17.25312ZM21.2188,28.1875L21.2188,19.8125L28.4688,23.96875L21.2188,28.1875Z" fill="#FFFFFF" fill-opacity="1"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 1000 B

BIN
src/assets/zhuashi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,42 @@
.bottomTab {
position: fixed;
bottom: 0;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 83px;
background-color: #000000;
z-index: 999;
li {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
span {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 12px;
font-weight: 500;
line-height: 24px;
text-align: right;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #adadad;
z-index: 1;
&.active {
color: #ffffff;
}
}
svg {
width: 24px;
height: 24px;
}
}
}

View File

@ -0,0 +1,53 @@
/*
* @LastEditors: John
* @Date: 2024-07-13 18:07:43
* @LastEditTime: 2024-07-19 15:42:36
* @Author: John
*/
import { useLocation, useNavigate } from "react-router-dom";
import classes from "./BottomTab-m.module.css";
import { AppOutline, HistogramOutline, TeamFill } from "antd-mobile-icons";
import { useEffect, useState } from "react";
export default function () {
const navigate = useNavigate();
const { pathname } = useLocation();
const [currentpathname, setCurrentpathname] = useState("");
useEffect(() => {
console.log("pathname:", pathname);
setCurrentpathname(pathname);
return () => {};
}, [pathname]);
return (
<>
<ul className={classes.bottomTab}>
<li onClick={() => navigate("/")}>
<AppOutline color={currentpathname == "/" ? "#ffffff" : "#adadad"} />
<span className={currentpathname == "/" ? classes.active : ""}>
Home
</span>
</li>
<li onClick={() => navigate("/friends")}>
<TeamFill
color={currentpathname == "/friends" ? "#ffffff" : "#adadad"}
/>
<span className={currentpathname == "/friends" ? classes.active : ""}>
Friends
</span>
</li>
<li onClick={() => navigate("/leaderboard")}>
<HistogramOutline
color={currentpathname == "/leaderboard" ? "#ffffff" : "#adadad"}
/>
<span
className={currentpathname == "/leaderboard" ? classes.active : ""}
>
Leaderboard
</span>
</li>
</ul>
</>
);
}

View File

@ -0,0 +1,30 @@
/* tslint:disable */
/* eslint-disable */
import React, { CSSProperties, SVGAttributes, FunctionComponent } from 'react';
import { getIconColor } from './helper';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
const DEFAULT_STYLE: CSSProperties = {
display: 'block',
};
const IconDianbao: FunctionComponent<Props> = ({ size = 18, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1024 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M390.39763376 902.25875678l15.55379104-230.47890347 418.53837681-376.11894674c18.38175303-16.96777203-4.24194301-25.45165805-28.27962004-9.89786702L280.10711554 610.9786702l-223.40899844-70.69905014c-48.07535409-14.13981003-48.07535409-46.6613731 11.31184802-70.69905014L936.19430083 134.46707225c39.59146809-18.38175303 77.76895517 9.89786702 62.21516414 70.69905013L851.35544067 902.25875678c-9.89786702 49.48933509-39.59146809 62.21516412-82.01089815 38.17748707L544.52156306 773.58648551 435.64502585 879.63506073c-12.72582902 12.72582902-22.62369604 22.62369604-45.24739209 22.62369605z m0 0"
fill={getIconColor(color, 0, '#333333')}
/>
</svg>
);
};
export default IconDianbao;

View File

@ -0,0 +1,30 @@
/* tslint:disable */
/* eslint-disable */
import React, { CSSProperties, SVGAttributes, FunctionComponent } from 'react';
import { getIconColor } from './helper';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
const DEFAULT_STYLE: CSSProperties = {
display: 'block',
};
const IconTuite: FunctionComponent<Props> = ({ size = 18, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1024 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M806.4 51.2h157.866667l-341.333334 392.533333L1024 972.8h-315.733333l-247.466667-324.266667-281.6 324.266667H21.333333L388.266667 554.666667 0 51.2h324.266667l221.866666 294.4 260.266667-294.4z m-55.466667 827.733333h85.333334L277.333333 136.533333H183.466667l567.466666 742.4z"
fill={getIconColor(color, 0, '#231815')}
/>
</svg>
);
};
export default IconTuite;

View File

@ -0,0 +1,12 @@
/* tslint:disable */
/* eslint-disable */
export const getIconColor = (color: string | string[] | undefined, index: number, defaultColor: string) => {
return color
? (
typeof color === 'string'
? color
: color[index] || defaultColor
)
: defaultColor;
};

View File

@ -0,0 +1,30 @@
/* tslint:disable */
/* eslint-disable */
import React, { SVGAttributes, FunctionComponent } from 'react';
import IconDianbao from './IconDianbao';
import IconTuite from './IconTuite';
export { default as IconDianbao } from './IconDianbao';
export { default as IconTuite } from './IconTuite';
export type IconNames = 'dianbao' | 'tuite';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
name: IconNames;
size?: number;
color?: string | string[];
}
const IconFont: FunctionComponent<Props> = ({ name, ...rest }) => {
switch (name) {
case 'dianbao':
return <IconDianbao {...rest} />;
case 'tuite':
return <IconTuite {...rest} />;
}
return null;
};
export default IconFont;

17
src/constants/index.ts Normal file
View File

@ -0,0 +1,17 @@
/*
* @LastEditors: John
* @Date: 2024-06-17 17:57:13
* @LastEditTime: 2024-07-13 13:59:23
* @Author: John
*/
export enum ASYNC_STORAGE_KEY {
Store = "user.store",
}
export enum Lang {
en = "en",
cn = "cn",
tw = "tw",
jp = "jp",
de = "de",
}

0
src/index.css Normal file
View File

28
src/main.tsx Normal file
View File

@ -0,0 +1,28 @@
/*
* @LastEditors: John
* @Date: 2024-07-13 10:21:58
* @LastEditTime: 2024-07-18 16:22:45
* @Author: John
*/
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { MainButton, WebAppProvider } from "@vkruglikov/react-telegram-web-app";
import { MemoryRouter } from "react-router-dom";
import VConsole from "vconsole";
import flexible from "./utils/flexible.ts";
import { getUrlParameterByName } from "./utils/index.ts";
// if (getUrlParameterByName("vconsole") === "1") {
new VConsole();
// }
flexible(window, document);
ReactDOM.createRoot(document.getElementById("root")!).render(
<WebAppProvider
options={{
smoothButtonsTransition: true,
}}
>
<App />
</WebAppProvider>
);

View File

@ -0,0 +1,159 @@
.frends {
display: flex;
flex-direction: column;
align-items: center;
.top_title {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 28px;
font-weight: 600;
line-height: normal;
text-align: center;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
z-index: 0;
margin-top: 35px;
}
.logo {
/* width: 141px; */
height: 149px;
margin-top: 31px;
background-image: url("../assets/kuang.svg");
background-repeat: no-repeat;
background-size: contain;
}
.tip {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 22px;
font-weight: normal;
line-height: normal;
text-align: center;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
z-index: 0;
margin-top: 61px;
}
.frends_list {
padding: 0 16px;
width: 100%;
display: flex;
flex-direction: column;
gap: 30px;
margin-top: 17px;
padding-bottom: 165px;
.frends_list_tabs {
display: flex;
gap: 10px;
.frends_list_title {
opacity: 1;
font-family: Roboto;
font-size: 20px;
font-weight: normal;
line-height: 24px;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #adadad;
&.frends_list_title_active {
color: #ffffff;
}
}
}
.frends_list_item {
display: flex;
align-items: center;
gap: 12px;
> svg {
width: 36px;
height: 36px;
}
span {
&:nth-of-type(1) {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 16px;
font-weight: normal;
line-height: normal;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
z-index: 1;
}
&:nth-of-type(2) {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 16px;
font-weight: normal;
line-height: normal;
text-align: right;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
z-index: 1;
margin-left: auto;
}
}
}
}
.bottom_btn {
padding: 17px 15px;
position: fixed;
bottom: 83px;
/* background-color: #000000; */
z-index: 999;
button {
width: 345px;
height: 48px;
border-radius: 0px;
opacity: 1;
background-image: url("../assets/big_buttom_bg.svg");
background-repeat: no-repeat;
background-size: contain;
> span {
opacity: 1;
font-family: Roboto;
font-size: 16px;
font-weight: 600;
line-height: 24px;
text-align: center;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #000000;
}
}
}
}

113
src/pages/Frends.tsx Normal file
View File

@ -0,0 +1,113 @@
/*
* @LastEditors: John
* @Date: 2024-07-13 16:08:30
* @LastEditTime: 2024-07-19 15:57:27
* @Author: John
*/
/*
* @LastEditors: John
* @Date: 2024-07-13 16:08:30
* @LastEditTime: 2024-07-15 15:31:58
* @Author: John
*/
import logo from "@/assets/logo.png";
import { Avatar, Button } from "antd-mobile";
import classes from "./Frends-m.module.css";
import { useWebApp } from "@vkruglikov/react-telegram-web-app";
import useUserStore from "@/store/User";
import { useEffect, useState } from "react";
import { api_homepage_subordinates_users } from "@/server/api";
import { subordinatesUsers } from "@/server/module";
import { cn } from "@/utils";
export default function () {
const WebApp = useWebApp();
const { InvitationCode } = useUserStore();
const [frends, setFrends] = useState<subordinatesUsers[]>();
const [currentLevel, setCurrentLevel] = useState(1);
useEffect(() => {
(async () => {
updateFriends(1);
})();
return () => {};
}, []);
async function updateFriends(level: number) {
const { data } = await api_homepage_subordinates_users().send({
queryParams: { level },
});
setFrends(data?.data);
}
return (
<>
<div className={classes.frends}>
<span className={classes.top_title}>
Invite friends and get more SCTT
</span>
<img className={classes.logo} src={logo} alt="" />
{frends?.length == 0 ? (
<span className={classes.tip}>
Tap on the button to invite your friends
</span>
) : (
<ul className={classes.frends_list}>
<div className={classes.frends_list_tabs}>
<span
className={cn(
classes.frends_list_title,
currentLevel == 1 ? classes.frends_list_title_active : ""
)}
onClick={() => {
setCurrentLevel(1);
updateFriends(1);
}}
>
Level 1
</span>
<span
className={cn(
classes.frends_list_title,
currentLevel == 2 ? classes.frends_list_title_active : ""
)}
onClick={() => {
setCurrentLevel(2);
updateFriends(2);
}}
>
Level 2
</span>
</div>
{frends?.map((v, i) => (
<FrendsItem key={i} userName={v.account} point={v.amount} />
))}
</ul>
)}
<div className={classes.bottom_btn}>
<Button
onClick={() => {
const url = `https://t.me/${import.meta.env.VITE_TG_BOT_NAME}/${
import.meta.env.VITE_TG_BOT_WEBAPP_NAME
}?startapp=${InvitationCode}`;
WebApp.openTelegramLink(`https://t.me/share/url?url=${url}`);
}}
fill="none"
>
Invite friends
</Button>
</div>
</div>
</>
);
}
function FrendsItem({ userName, point }: { userName: string; point: string }) {
return (
<li className={classes.frends_list_item}>
<Avatar src="" />
<span>{userName}</span>
<span>+{point} SCTT</span>
</li>
);
}

View File

@ -0,0 +1,31 @@
.guide {
height: 100%;
background-image: url("../assets/guide_bg.svg");
background-repeat: no-repeat;
background-size: cover;
background-position: 0 70%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
> img {
width: 244.02px;
height: 207.05px;
}
> span {
opacity: 1;
font-family: Roboto;
font-size: 30px;
font-weight: 600;
line-height: 44px;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
margin-top: 20px;
}
}

40
src/pages/Guide.tsx Normal file
View File

@ -0,0 +1,40 @@
/*
* @LastEditors: John
* @Date: 2024-07-18 14:42:58
* @LastEditTime: 2024-07-19 17:53:42
* @Author: John
*/
import {
MainButton,
useInitData,
useWebApp,
} from "@vkruglikov/react-telegram-web-app";
import classes from "./Guide-m.module.css";
import { useEffect, useState } from "react";
import guide_icon from "@/assets/guide_icon.svg";
import dianbao from "@/assets/dianbao.svg";
import youguang from "@/assets/youguang.svg";
import tuite from "@/assets/tuite.svg";
import logo from "@/assets/logo.svg";
import { api_login } from "@/server/api";
import useUserStore from "@/store/User";
export default function () {
const WebApp = useWebApp();
const [step, setStep] = useState(1);
const [initDataUnsafe, initData] = useInitData();
const { Token, UpdateToken, UpdateInvitationCode } = useUserStore();
useEffect(() => {
return () => {};
}, []);
return (
<>
<div className={classes.guide}>
<img src={logo} alt="" />
<span>LOADING...</span>
</div>
</>
);
}

236
src/pages/Home-m.module.css Normal file
View File

@ -0,0 +1,236 @@
.home {
display: flex;
flex-direction: column;
align-items: center;
padding-bottom: 94px;
.home_top {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 65px;
padding: 21px 0px;
gap: 6px;
background-image: url("../assets/home_top_bg.svg");
background-size: contain;
> svg {
width: 20px;
height: 20px;
}
> span {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 16px;
font-weight: 500;
line-height: 24px;
text-align: center;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
z-index: 1;
}
}
.logo {
width: 171px;
height: 126px;
margin-top: 35px;
}
.pens {
opacity: 1;
font-family: Roboto;
font-size: 32px;
font-weight: 600;
line-height: 44px;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
margin-top: 14px;
}
.community {
width: 345px;
height: 224px;
border-radius: 16px;
opacity: 1;
background: #1e1e1e;
padding: 17px 16px;
display: flex;
flex-direction: column;
margin-top: 20px;
.community_title {
opacity: 1;
font-family: Roboto;
font-size: 20px;
font-weight: 600;
line-height: 24px;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
margin-bottom: 28px;
}
.community_item {
display: flex;
align-items: center;
gap: 8px;
> img {
width: 32px;
height: 32px;
}
> span {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 16px;
font-weight: normal;
line-height: 24px;
text-transform: capitalize;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
z-index: 1;
}
> button {
width: 68px;
height: 32px;
border-radius: 30px;
opacity: 1;
background: #e2c72d;
margin-left: auto;
display: flex;
align-items: center;
> span {
opacity: 1;
font-family: Roboto;
font-size: 16px;
font-weight: normal;
line-height: 24px;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #000000;
}
}
}
.community_item_bottom {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 12px;
font-weight: normal;
line-height: 24px;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #adadad;
z-index: 1;
margin-top: 4px;
}
ul {
display: flex;
flex-direction: column;
gap: 18px;
}
}
.join_card {
}
.rewards {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
width: 100%;
.rewards_title {
align-self: flex-start;
margin: 0 15px;
margin-top: 25px;
opacity: 1;
opacity: 1;
font-family: Roboto;
font-size: 18px;
font-weight: 600;
line-height: 24px;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
}
.reward_list_item {
display: flex;
align-items: center;
gap: 14px;
padding: 0 15px;
width: 345px;
height: 68px;
border-radius: 12px;
opacity: 1;
background: #18181b;
box-sizing: border-box;
border: 1px solid #27272a;
> span {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 16px;
font-weight: 500;
line-height: 24px;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
z-index: 1;
&:last-of-type {
margin-left: auto;
}
}
> svg {
width: 19px;
height: 19px;
}
}
}
}

139
src/pages/Home.tsx Normal file
View File

@ -0,0 +1,139 @@
/*
* @LastEditors: John
* @Date: 2024-07-13 10:50:24
* @LastEditTime: 2024-07-19 18:46:32
* @Author: John
*/
import classes from "./Home-m.module.css";
import { Routes, useNavigate } from "react-router-dom";
import home_top_bg from "@/assets/home_top_bg.svg";
import tg_white from "@/assets/tg_white.svg";
import tuite_white from "@/assets/tuite_white.svg";
import logo from "@/assets/logo.svg";
import {
CheckOutline,
PlayOutline,
StarOutline,
UserAddOutline,
} from "antd-mobile-icons";
import { Button } from "antd-mobile";
import { useEffect, useState } from "react";
import {
api_follow_twitter,
api_homepage_query_user_income,
api_query_task_configuration_list,
api_query_whether_the_user_receives_the_registration_reward,
api_start_task,
} from "@/server/api";
import { useWebApp } from "@vkruglikov/react-telegram-web-app";
import IconFont from "@/components/iconfont";
import { taskConfigurationListItem } from "@/server/module";
import arrow_right_top from "@/assets/arrow_right_top.svg";
export default function () {
const WebApp = useWebApp();
const navigate = useNavigate();
const [totalPoint, setTotalPoint] = useState(0);
const [signReward, setSignReward] = useState<string | undefined>();
const [tgReward, setTgReward] = useState<string | undefined>();
const [xReward, setXReward] = useState<string | undefined>();
const [myReward, setMyReward] = useState<string | undefined>();
const [inviteReward, setInviteReward] = useState<string | undefined>();
const [tasksList, setTasksList] = useState<taskConfigurationListItem[]>();
useEffect(() => {
WebApp.setHeaderColor("#000000");
(async () => {
UpdateHomeData();
UpdateTaskList();
})();
return () => {};
}, []);
async function UpdateHomeData() {
const { data: incomeRes } = await api_homepage_query_user_income().send({});
setMyReward(incomeRes?.data.wordRewar);
setInviteReward(incomeRes?.data.teamRewar);
setTotalPoint(
parseInt(`${incomeRes?.data.wordRewar || 0}`) +
parseInt(`${incomeRes?.data.teamRewar || 0}`)
);
}
async function UpdateTaskList() {
const { data } = await api_query_task_configuration_list().send({});
setTasksList(data?.data);
}
return (
<>
<div className={classes.home}>
<div className={classes.home_top}>
<PlayOutline color="#fff" />
<span>Your Score</span>
</div>
<img src={logo} alt="" className={classes.logo} />
<span className={classes.pens}>{totalPoint || 0} SCTT</span>
<div className={classes.community}>
<span className={classes.community_title}>PenguinCoop Community</span>
<ul>
<CommunityItem
title="Join Now"
buttom_text="Join Duckcoop Channel (+2,500 DUCKS)"
/>
<CommunityItem
title="Join Now"
buttom_text="Join Duckcoop Community (+2,500 DUCKS)"
/>
</ul>
</div>
<div className={classes.rewards}>
<span className={classes.rewards_title}>Your rewards</span>
<div className={classes.reward_list_item}>
<StarOutline color="#ffffff" />
<span>Your rewards</span>
<span>+{myReward || 0} SCTT</span>
</div>
<div className={classes.reward_list_item}>
<IconFont name="dianbao" color="#ffffff" />
<span>Invite friends</span>
<span>{inviteReward || 0}</span>
</div>
</div>
</div>
</>
);
}
function CommunityItem({
title,
task_id,
task_url,
type,
buttom_text,
button_disable,
onTaskSuccess,
}: {
task_id?: string;
task_url?: string;
title: string;
type?: taskConfigurationListItem["type"];
buttom_text: string;
button_disable?: boolean;
onTaskSuccess?: () => void;
}) {
const WebApp = useWebApp();
return (
<li>
<div className={classes.community_item}>
<img src={arrow_right_top} alt="" />
<span>{title}</span>
<Button fill="none">Check</Button>
</div>
<span className={classes.community_item_bottom}>{buttom_text}</span>
</li>
);
}

View File

@ -0,0 +1,9 @@
.index {
display: flex;
flex-direction: column;
height: 100%;
.container {
flex: auto;
position: relative;
}
}

49
src/pages/Index.tsx Normal file
View File

@ -0,0 +1,49 @@
import { Route, Routes, useNavigate } from "react-router-dom";
import Home from "./Home";
import { Badge, TabBar } from "antd-mobile";
import {
AppOutline,
MessageFill,
MessageOutline,
UnorderedListOutline,
UserOutline,
} from "antd-mobile-icons";
import classes from "./Index-m.module.css";
import Leaderboard from "./Leaderboard";
import Frends from "./Frends";
import BottomTab from "@/components/BottomTab";
const tabs = [
{
key: "0",
title: "Home",
icon: <AppOutline />,
},
{
key: "1",
title: "Leaderboard",
icon: <UnorderedListOutline />,
},
{
key: "2",
title: "friends",
icon: <MessageOutline />,
},
];
export default function () {
const navigate = useNavigate();
return (
<>
<div className={classes.index}>
<div className={classes.container}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/leaderboard" element={<Leaderboard />} />
<Route path="/friends" element={<Frends />} />
</Routes>
</div>
<BottomTab />
</div>
</>
);
}

View File

@ -0,0 +1,244 @@
.leaderboard {
display: flex;
flex-direction: column;
align-items: center;
.top_title {
opacity: 1;
font-family: Roboto;
font-size: 32px;
font-weight: 600;
line-height: normal;
text-align: center;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
margin-top: 41px;
}
.rank_data {
width: 345px;
height: 68px;
border-radius: 12px;
opacity: 1;
/* 自动布局 */
display: flex;
align-items: center;
padding: 13px 15px;
/* background: #1e1e1e; */
background-image: url("../assets/user_rank_bg.svg");
background-repeat: no-repeat;
background-size: contain;
border-radius: 0px;
gap: 12px;
margin-top: 38px;
> div {
display: flex;
flex-direction: column;
> span {
&:nth-of-type(1) {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 16px;
font-weight: 600;
line-height: normal;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
z-index: 0;
}
&:nth-of-type(2) {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 16px;
font-weight: 600;
line-height: normal;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #adadad;
z-index: 1;
}
}
}
> span {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 16px;
font-weight: 600;
line-height: normal;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
z-index: 1;
margin-left: auto;
}
}
> button {
margin-top: 16px;
width: 344px;
height: 48px;
border-radius: 8px;
opacity: 1;
/* 自动布局 */
display: flex;
padding: 12px 115px;
background: #261138;
> span {
display: flex;
align-items: center;
gap: 5px;
> svg {
width: 24px;
height: 24px;
}
> span {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 16px;
font-weight: 600;
line-height: normal;
text-align: center;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
z-index: 1;
white-space: nowrap;
}
}
}
.rank_title {
align-self: flex-start;
opacity: 1;
font-family: Roboto;
font-size: 20px;
font-weight: 600;
line-height: 24px;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
margin: 0 15px;
margin-top: 40px;
}
.rank_list {
width: 100%;
padding: 0 15px;
padding-bottom: 94px;
margin-top: 32px;
display: flex;
flex-direction: column;
gap: 32px;
.rank_item {
display: flex;
align-items: center;
gap: 12px;
> svg {
width: 36px;
height: 36px;
}
> div {
display: flex;
flex-direction: column;
gap: 4px;
> span {
&:nth-of-type(1) {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 16px;
font-weight: 600;
line-height: normal;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
z-index: 0;
max-width: 100px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
&:nth-of-type(2) {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 16px;
font-weight: 600;
line-height: normal;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #adadad;
z-index: 1;
}
}
}
.rank_item_icon {
width: 24px;
height: 24px;
margin-left: auto;
}
.rank_index {
/* 自动布局子元素 */
opacity: 1;
font-family: Roboto;
font-size: 16px;
font-weight: 600;
line-height: 24px;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
z-index: 1;
margin-left: auto;
}
}
}
}

89
src/pages/Leaderboard.tsx Normal file
View File

@ -0,0 +1,89 @@
/*
* @LastEditors: John
* @Date: 2024-07-13 16:08:04
* @LastEditTime: 2024-07-19 14:55:57
* @Author: John
*/
import { Avatar, Button } from "antd-mobile";
import classes from "./Leaderboard-m.module.css";
import { StarOutline } from "antd-mobile-icons";
import rank1 from "@/assets/rank1.svg";
import rank2 from "@/assets/rank2.svg";
import rank3 from "@/assets/rank3.svg";
import { useEffect, useState } from "react";
import { api_ranking } from "@/server/api";
import { RewardVo } from "@/server/module";
export default function () {
const [userRank, setUserRank] = useState<RewardVo>();
const [ranks, setRanks] = useState<RewardVo[]>();
const [totalUser, setTotalUser] = useState<number>();
useEffect(() => {
(async () => {
const { data } = await api_ranking().send({});
setUserRank(data?.data.rewardVo);
setRanks(data?.data.rewardVos);
setTotalUser(data?.data.numberOfUsers);
})();
return () => {};
}, []);
return (
<>
<div className={classes.leaderboard}>
<span className={classes.top_title}>Telegram Wall of Fame</span>
<div className={classes.rank_data}>
<Avatar src="" />
<div>
<span>{userRank?.tgName}</span>
<span>{userRank?.amount || 0} SCTT</span>
</div>
<span>#{userRank?.ranking}</span>
</div>
<span className={classes.rank_title}>{totalUser || 0} holders</span>
<ul className={classes.rank_list}>
{ranks?.map((v, i) => (
<RankItem
userName={v.tgName}
point={v.amount}
key={i}
index={i + 1}
/>
))}
</ul>
</div>
</>
);
}
function RankItem({
userName,
point,
index,
}: {
userName: string;
point: string;
index: number;
}) {
return (
<li className={classes.rank_item}>
<Avatar src="" />
<div>
<span>{userName}</span>
<span>{point} SCTT</span>
</div>
{index == 1 && (
<img className={classes.rank_item_icon} src={rank1} alt="" />
)}
{index == 2 && (
<img className={classes.rank_item_icon} src={rank2} alt="" />
)}
{index == 3 && (
<img className={classes.rank_item_icon} src={rank3} alt="" />
)}
{index > 3 && <span className={classes.rank_index}>#{index}</span>}
</li>
);
}

122
src/server/api.ts Normal file
View File

@ -0,0 +1,122 @@
/*
* @LastEditors: John
* @Date: 2024-07-15 10:35:20
* @LastEditTime: 2024-07-19 15:50:19
* @Author: John
*/
import { GET, POST } from "./client";
import {
RewardVo,
subordinatesUsers,
taskConfigurationListItem,
userBenefits,
} from "./module";
// 登录
export function api_login() {
return POST<
{
initData: string;
invitationCode?: string;
},
{ token: string }
>({
url: "/api/account/signIn",
});
}
// 查询用户是否领取注册奖励
export function api_query_whether_the_user_receives_the_registration_reward() {
return GET<any, string>({
url: "/api/reward/signUpForIncentives",
});
}
// 领取奖励
export function api_receive_rewards() {
return POST<any, { reward: string; year: number }, { status?: number }>({
url: "/api/reward/claimYourRewards",
});
}
// 排行
export function api_ranking() {
return GET<
any,
{
rewardVo: RewardVo;
rewardVos: RewardVo[];
numberOfUsers: number;
}
>({
url: "/api/reward/rankingWalletLog",
});
}
// 首页查询用户收益
export function api_homepage_query_user_income() {
return GET<any, { wordRewar: string; teamRewar: string }>({
url: "/api/reward/userBenefits",
});
}
// 获取用户信息
export function api_get_user_information() {
return GET<
any,
{
account: string;
accountType: number;
allPid: string;
chainType: number;
codePrompt: number;
createTime: string;
flag: number;
id: string;
level: number;
minLevel: number;
mintNumber: number;
passwordLogin: string;
passwordPay: string;
presidentNumber: number;
referId: string;
shareCode: string;
shareNum: number;
teamNum: number;
uid: string;
updateTime: string;
userImg: string;
userType: number;
}
>({
url: "/api/user/findUser",
});
}
// 首页下级用户
export function api_homepage_subordinates_users() {
return GET<{ level: number }, subordinatesUsers[]>({
url: "/api/reward/subordinateUsers",
});
}
// 关注推特
export function api_follow_twitter() {
return POST<any, {}>({
url: "/api/reward/followTwitter",
});
}
// 查询任务配置列表
export function api_query_task_configuration_list() {
return GET<any, taskConfigurationListItem[]>({
url: "/api/task-config",
});
}
export function api_start_task() {
return POST<any, {}, { id: string }>({
url: "/api/task-config",
});
}

76
src/server/client.ts Normal file
View File

@ -0,0 +1,76 @@
/*
* @LastEditors: John
* @Date: 2024-06-18 10:09:21
* @LastEditTime: 2024-07-15 11:30:25
* @Author: John
*/
import { Client } from "@hyper-fetch/core";
import useUserStore from "@/store/User";
import { Lang } from "@/constants";
import { Toast } from "antd-mobile";
import { BASE_RESPONSE } from "./module";
import { api_login } from "./api";
import { useInitData, useWebApp } from "@vkruglikov/react-telegram-web-app";
function initClient({ catchErr }: { catchErr: boolean }) {
return new Client({ url: import.meta.env.VITE_BASE_API_URL })
.onAuth(async (req) => {
const headers = {
...req.headers,
Authorization: useUserStore.getState().Token,
};
return req.setHeaders(headers);
})
.onResponse((res) => {
console.log(res);
if (!res.success) {
Toast.clear();
Toast.show({ content: "server error", icon: "fail" });
throw new Error(res.error?.message);
}
const resData: BASE_RESPONSE = res.data;
if (resData.code !== 200 && resData.code !== 0) {
if (resData.msg) Toast.show({ content: resData.msg, icon: "fail" });
if (catchErr) return res;
throw new Error(resData.msg || "client on response error");
}
return res;
});
}
export const POST = <P = any, R = any, QueryParams = any>({
url,
catchErr = false,
}: {
url: string;
catchErr?: boolean;
}) => {
return initClient({ catchErr }).createRequest<
BASE_RESPONSE<R>,
P,
any,
QueryParams
>()({
method: "POST",
endpoint: url,
});
};
export const GET = <P = any, R = any>({
url,
requiresToken = true,
catchErr = false,
}: {
url: string;
requiresToken?: boolean;
catchErr?: boolean;
}) => {
return initClient({ catchErr }).createRequest<
BASE_RESPONSE<R>,
any,
any,
P
>()({
method: "GET",
endpoint: url,
});
};

68
src/server/module.d.ts vendored Normal file
View File

@ -0,0 +1,68 @@
export type BASE_RESPONSE<T = any> = {
code: 0 | 200;
data: T;
msg: string;
timeMillis: number;
}; // What's returned from request
export type RewardVo = {
tgName: string;
amount: string;
ranking: number;
};
export type userBenefits = {
coinId: number;
createTime: string;
extRemark: string;
flag: number;
id: number;
memberId: string;
opAfter: string;
opBefore: string;
opRemark: string;
opType: number;
opValue: string;
type: number;
updateTime: string;
walletId: number;
};
export type subordinatesUsers = {
account: string;
accountType: number;
allPid: string;
amount: string;
chainType: number;
codePrompt: number;
createTime: string;
flag: number;
id: string;
level: number;
minLevel: number;
mintNumber: number;
passwordLogin: string;
passwordPay: string;
presidentNumber: number;
referId: string;
shareCode: string;
shareNum: number;
teamNum: number;
uid: string;
updateTime: string;
userImg: string;
userType: number;
};
export type taskConfigurationListItem = {
createTime: string;
externalId: string;
flag: 0 | 1; // 标记删除0 / 1
id: string;
opValue: string;
status: 0 | 1 | 2; // 0:已下架(前端不在显示) 1:进行中 2:已结束
taskType: 0 | 1; // 0:任务为完成 1任务已完成
taskUrl: string;
type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; // 1:加入TG频道 2:加入TG群 3:直推积分 4:间推积分 5:关注推特 6:推特点赞 7:转发推特 8:引用推特
updateTime: string;
};

36
src/store/User.ts Normal file
View File

@ -0,0 +1,36 @@
/*
* @LastEditors: John
* @Date: 2024-06-17 17:45:43
* @LastEditTime: 2024-07-17 11:24:15
* @Author: John
*/
import { ASYNC_STORAGE_KEY, Lang } from "@/constants";
import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
interface UserState {
Token: string;
UpdateToken: (t: string) => void;
InvitationCode: string;
UpdateInvitationCode: (i: string) => void;
}
export const useUserStore = create<UserState>()(
persist(
(set, _get) => ({
Token: "",
UpdateToken: (t) => set({ Token: t }),
InvitationCode: "",
UpdateInvitationCode(i) {
set({ InvitationCode: i });
},
}),
{
name: ASYNC_STORAGE_KEY.Store, // name of item in the storage (must be unique)
storage: createJSONStorage(() => sessionStorage), // (optional) by default the 'localStorage' is used
}
)
);
export default useUserStore;

51
src/utils/flexible.ts Normal file
View File

@ -0,0 +1,51 @@
/*
* @LastEditors: John
* @Date: 2024-01-09 09:34:24
* @LastEditTime: 2024-07-13 14:13:21
* @Author: John
*/
export default function flexible(window: Window, document: Document) {
var docEl = document.documentElement;
var dpr = window.devicePixelRatio || 1;
// adjust body font size
function setBodyFontSize() {
if (document.body) {
document.body.style.fontSize = 12 * dpr + "px";
} else {
document.addEventListener("DOMContentLoaded", setBodyFontSize);
}
}
setBodyFontSize();
// set 1rem = viewWidth / 10
function setRemUnit() {
var rem = docEl.clientWidth / 10;
// console.log("rem:", rem);
docEl.style.fontSize = rem + "px";
}
setRemUnit();
// reset rem unit on page resize
window.addEventListener("resize", setRemUnit);
window.addEventListener("pageshow", function (e) {
if (e.persisted) {
setRemUnit();
}
});
// detect 0.5px supports
if (dpr >= 2) {
var fakeBody = document.createElement("body");
var testElement = document.createElement("div");
testElement.style.border = ".5px solid transparent";
fakeBody.appendChild(testElement);
docEl.appendChild(fakeBody);
if (testElement.offsetHeight === 1) {
docEl.classList.add("hairlines");
}
docEl.removeChild(fakeBody);
}
}

16
src/utils/index.ts Normal file
View File

@ -0,0 +1,16 @@
import { twMerge } from "tailwind-merge";
import { type ClassValue, clsx } from "clsx";
export function getUrlParameterByName(name: string, url?: string) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
let regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return "";
console.log("url params:", results);
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

17
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
/*
* @LastEditors: John
* @Date: 2024-07-13 10:21:58
* @LastEditTime: 2024-07-16 16:27:31
* @Author: John
*/
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_BASE_URL: string;
readonly VITE_BASE_API_URL: string;
readonly VITE_TG_COMMUNITY: string;
readonly VITE_TG_BOT_NAME: string;
readonly VITE_TG_BOT_WEBAPP_NAME: string;
// 更多环境变量...
readonly MODE: "development" | "production" | "test";
}

32
tsconfig.json Normal file
View File

@ -0,0 +1,32 @@
/*
* @LastEditors: John
* @Date: 2024-06-17 17:20:03
* @LastEditTime: 2024-06-24 18:48:07
* @Author: John
*/
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"allowSyntheticDefaultImports": true,
"strict": true,
"paths": {
"@/*": ["./src/*"]
}
},
"references": [{ "path": "./tsconfig.node.json" }]
}

12
tsconfig.node.json Normal file
View File

@ -0,0 +1,12 @@
{
"compilerOptions": {
"jsx": "react",
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strict": true
},
"include": ["./vite.config.ts"]
}

2
vite.config.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
declare const _default: import("vite").UserConfig;
export default _default;

32
vite.config.js Normal file
View File

@ -0,0 +1,32 @@
/*
* @LastEditors: John
* @Date: 2024-07-13 10:21:58
* @LastEditTime: 2024-07-19 17:08:59
* @Author: John
*/
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";
// https://vitejs.dev/config/
export default defineConfig({
server: {
host: "192.168.10.167",
port: 8083,
proxy: {
"/dev": {
target: "http://192.168.10.100:8096",
changeOrigin: true,
rewrite: function (path) {
return path.replace(/^\/dev/, "");
},
},
},
},
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
css: {},
});

3823
yarn.lock Normal file

File diff suppressed because it is too large Load Diff