皮肤上传

This commit is contained in:
xmdhs 2023-10-03 22:38:07 +08:00
parent 6b489789d3
commit ab5e87ef42
No known key found for this signature in database
GPG Key ID: E809D6D43DEFCC95
8 changed files with 63 additions and 40 deletions

View File

@ -4,6 +4,7 @@ import Login from '@/views/Login'
import Register from '@/views/Register' import Register from '@/views/Register'
import Profile from '@/views/profile/Profile' import Profile from '@/views/profile/Profile'
import Textures from '@/views/profile/Textures' import Textures from '@/views/profile/Textures'
import Security from '@/views/profile/Security'
import Layout from '@/views/Layout' import Layout from '@/views/Layout'
const router = createBrowserRouter([ const router = createBrowserRouter([
@ -19,7 +20,7 @@ function Root() {
<Route path="/register" element={<Register />} /> <Route path="/register" element={<Register />} />
<Route path="/profile" element={<Profile />} /> <Route path="/profile" element={<Profile />} />
<Route path="/textures" element={<Textures />} /> <Route path="/textures" element={<Textures />} />
<Route path="/security" element={<Security />} />
</Route> </Route>
</Routes> </Routes>
<ScrollRestoration /> <ScrollRestoration />

View File

@ -54,3 +54,20 @@ export async function yggProfile(uuid: string) {
} }
return data as YggProfile return data as YggProfile
} }
export async function upTextures(uuid: string, token: string, textureType: 'skin' | 'cape', model: 'slim' | '', file: File) {
const f = new FormData()
f.set("file", file)
f.set("model", model)
const r = await fetch(import.meta.env.VITE_APIADDR + "/api/yggdrasil/api/user/profile/" + uuid + "/" + textureType, {
method: "PUT",
body: f,
headers: {
"Authorization": "Bearer " + token
}
})
if (r.status != 204) {
throw new Error("上传失败 " + String(r.status))
}
}

View File

@ -2,7 +2,7 @@ export interface tokenData {
accessToken: string accessToken: string
selectedProfile: { selectedProfile: {
name: string name: string
uuid: string id: string
} }
} }

View File

@ -110,7 +110,7 @@ const MyToolbar = memo(function MyToolbar() {
setAnchorEl(null); setAnchorEl(null);
setNowUser({ name: "", uuid: "" }) setNowUser({ name: "", uuid: "" })
setToken("") setToken("")
navigate("/") navigate("/login")
}) })
useTilg() useTilg()
@ -240,7 +240,7 @@ const MyDrawer = function MyDrawer() {
{ {
icon: <SecurityIcon />, icon: <SecurityIcon />,
title: '安全设置', title: '安全设置',
link: '/setting' link: '/security'
} }
] as ListItem[], []) ] as ListItem[], [])

View File

@ -49,7 +49,7 @@ export default function SignIn() {
if (!v) return if (!v) return
setToken(v.accessToken) setToken(v.accessToken)
setUserInfo({ setUserInfo({
uuid: v.selectedProfile.uuid, uuid: v.selectedProfile.id,
name: v.selectedProfile.name, name: v.selectedProfile.name,
}) })
navigate("/profile") navigate("/profile")

View File

@ -5,10 +5,9 @@ import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import CardHeader from '@mui/material/CardHeader'; import CardHeader from '@mui/material/CardHeader';
import { useHover, useRequest, useUnmount } from 'ahooks'; import { useHover, useRequest, useUnmount } from 'ahooks';
import { ApiErr } from '@/apis/error'; import { LayoutAlertErr, user } from '@/store/store';
import { LayoutAlertErr, token } from '@/store/store';
import { useAtomValue, useSetAtom } from 'jotai'; import { useAtomValue, useSetAtom } from 'jotai';
import { userInfo, yggProfile } from '@/apis/apis'; import { yggProfile } from '@/apis/apis';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
@ -22,32 +21,21 @@ import useTilg from 'tilg';
import useTitle from '@/hooks/useTitle'; import useTitle from '@/hooks/useTitle';
const Profile = function Profile() { const Profile = function Profile() {
const nowToken = useAtomValue(token)
const navigate = useNavigate(); const navigate = useNavigate();
const setErr = useSetAtom(LayoutAlertErr) const setErr = useSetAtom(LayoutAlertErr)
const userinfo = useAtomValue(user)
const [textures, setTextures] = useState({ skin: "", cape: "", model: "default" }) const [textures, setTextures] = useState({ skin: "", cape: "", model: "default" })
useTitle("个人信息") useTitle("个人信息")
const userinfo = useRequest(() => userInfo(nowToken), {
refreshDeps: [nowToken],
cacheKey: "/api/v1/user" + nowToken,
staleTime: 60000,
onError: e => {
if (e instanceof ApiErr && e.code == 5) {
navigate("/login")
}
console.warn(e)
setErr(String(e))
}
})
const SkinInfo = useRequest(() => yggProfile(userinfo.data?.uuid ?? ""), { const SkinInfo = useRequest(() => yggProfile(userinfo.uuid ?? ""), {
cacheKey: "/api/yggdrasil/sessionserver/session/minecraft/profile/" + userinfo.data?.uuid, cacheKey: "/api/yggdrasil/sessionserver/session/minecraft/profile/" + userinfo?.uuid,
onError: e => { onError: e => {
console.warn(e) console.warn(e)
setErr(String(e)) setErr(String(e))
}, },
refreshDeps: [userinfo.data?.uuid], refreshDeps: [userinfo?.uuid],
}) })
useEffect(() => { useEffect(() => {
@ -70,12 +58,10 @@ const Profile = function Profile() {
<Card sx={{ gridArea: "a" }}> <Card sx={{ gridArea: "a" }}>
<CardHeader title="信息" /> <CardHeader title="信息" />
<CardContent sx={{ display: "grid", gridTemplateColumns: "4em auto" }}> <CardContent sx={{ display: "grid", gridTemplateColumns: "4em auto" }}>
<Typography>uid</Typography>
<Typography sx={{ wordBreak: 'break-all' }}>{(userinfo.loading && !userinfo.data) ? <Skeleton /> : userinfo.data?.uid}</Typography>
<Typography>name</Typography> <Typography>name</Typography>
<Typography>{(SkinInfo.loading || userinfo.loading) && !SkinInfo.data ? <Skeleton /> : SkinInfo.data?.name}</Typography> <Typography>{userinfo.name}</Typography>
<Typography>uuid</Typography> <Typography>uuid</Typography>
<Typography sx={{ wordBreak: 'break-all' }}>{(userinfo.loading && !userinfo.data) ? <Skeleton /> : userinfo.data?.uuid}</Typography> <Typography sx={{ wordBreak: 'break-all' }}>{userinfo.uuid}</Typography>
</CardContent> </CardContent>
{/* <CardActions> {/* <CardActions>
<Button size="small"></Button> <Button size="small"></Button>

View File

@ -0,0 +1,6 @@
export default function Security() {
return (<>
<p></p>
</>)
}

View File

@ -16,45 +16,48 @@ import Box from "@mui/material/Box";
import ReactSkinview3d from "react-skinview3d"; import ReactSkinview3d from "react-skinview3d";
import { useUnmount } from "ahooks"; import { useUnmount } from "ahooks";
import { SkinViewer } from "skinview3d"; import { SkinViewer } from "skinview3d";
import { useAtomValue, useSetAtom } from "jotai";
import { LayoutAlertErr, token, user } from "@/store/store";
import { upTextures } from "@/apis/apis";
import Loading from "@/components/Loading";
const Textures = function Textures() { const Textures = function Textures() {
const [redioValue, setRedioValue] = useState("skin") const [redioValue, setRedioValue] = useState("skin")
useTitle("上传皮肤") useTitle("上传皮肤")
const [file, setFile] = useState<File | null>(null) const [file, setFile] = useState<File | null>(null)
const skin = useRef({ const skin = useRef("")
skinUrl: "",
capeUrl: "",
})
const skinview3dView = useRef<SkinViewer | null>(null); const skinview3dView = useRef<SkinViewer | null>(null);
const setErr = useSetAtom(LayoutAlertErr)
const [loading, setLoading] = useState(false)
const userinfo = useAtomValue(user)
const nowToken = useAtomValue(token)
useUnmount(() => { useUnmount(() => {
skin.current.skinUrl && URL.revokeObjectURL(skin.current.skinUrl) skin.current && URL.revokeObjectURL(skin.current)
skin.current.capeUrl && URL.revokeObjectURL(skin.current.capeUrl)
skinview3dView.current?.dispose() skinview3dView.current?.dispose()
}) })
useEffect(() => { useEffect(() => {
if (file) { if (file) {
const nu = URL.createObjectURL(file) const nu = URL.createObjectURL(file)
skin.current.skinUrl && URL.revokeObjectURL(skin.current.skinUrl) skin.current && URL.revokeObjectURL(skin.current)
skin.current.capeUrl && URL.revokeObjectURL(skin.current.capeUrl)
skinview3dView.current?.loadSkin(null) skinview3dView.current?.loadSkin(null)
skinview3dView.current?.loadCape(null) skinview3dView.current?.loadCape(null)
switch (redioValue) { switch (redioValue) {
case "skin": case "skin":
skin.current.skinUrl = nu skin.current = nu
skinview3dView.current?.loadSkin(nu, { model: "default" }).then(() => skinview3dView.current?.loadSkin(nu, { model: "default" }).then(() =>
skinview3dView.current?.loadSkin(nu, { model: "default" }) skinview3dView.current?.loadSkin(nu, { model: "default" })
) )
break break
case "slim": case "slim":
skin.current.skinUrl = nu skin.current = nu
skinview3dView.current?.loadSkin(nu, { model: "slim" }).then(() => skinview3dView.current?.loadSkin(nu, { model: "slim" }).then(() =>
skinview3dView.current?.loadSkin(nu, { model: "slim" }) skinview3dView.current?.loadSkin(nu, { model: "slim" })
) )
break break
case "cape": case "cape":
skin.current.capeUrl = nu skin.current = nu
skinview3dView.current?.loadCape(nu).then(() => { skinview3dView.current?.loadCape(nu).then(() => {
skinview3dView.current?.loadCape(nu) skinview3dView.current?.loadCape(nu)
}) })
@ -70,6 +73,15 @@ const Textures = function Textures() {
setFile(newFile) setFile(newFile)
} }
const handleToUpload = () => {
if (!file || loading) return
setLoading(true)
const textureType = redioValue == "cape" ? "cape" : "skin"
const model = redioValue == "slim" ? "slim" : ""
upTextures(userinfo.uuid, nowToken, textureType, model, file).catch(e => [setErr(String(e)), console.warn(e)]).
finally(() => setLoading(false))
}
useTilg() useTilg()
return (<> return (<>
@ -98,7 +110,7 @@ const Textures = function Textures() {
</FormControl> </FormControl>
</CardContent> </CardContent>
<CardActions> <CardActions>
<Button variant="contained" sx={{ maxWidth: "3em" }}></Button> <Button variant="contained" sx={{ maxWidth: "3em" }} onClick={handleToUpload}></Button>
</CardActions> </CardActions>
</Card> </Card>
<Card sx={{ gridArea: "b" }}> <Card sx={{ gridArea: "b" }}>
@ -114,6 +126,7 @@ const Textures = function Textures() {
</CardContent> </CardContent>
</Card> </Card>
</Box> </Box>
{loading && <Loading />}
</>) </>)
} }