feat(account): implement initial skin loading in profile

This commit is contained in:
2025-06-17 14:49:19 +03:00
parent 8f4996fb2b
commit f9e0c068f4
4 changed files with 85 additions and 4 deletions

7
src/api/user.ts Normal file
View File

@@ -0,0 +1,7 @@
import apiClient from "./axios";
import type { SessionProfileResponse } from "@/types";
export const getUserProfile = (uuid: string) => {
// Этот эндпоинт не начинается с /api, поэтому указываем полный путь
return apiClient.get<SessionProfileResponse>(`/sessionserver/session/minecraft/profile/${uuid}`);
};

View File

@@ -1,6 +1,7 @@
import { defineStore } from "pinia";
import { ref, computed } from "vue";
import { registerUser as apiRegisterUser } from "@/api/auth";
import { getUserProfile as apiGetUserProfile } from "@/api/user";
import type { RegisterRequest } from "@/types";
import router from "@/router";
import apiClient from "@/api/axios";
@@ -9,6 +10,7 @@ export const useAuthStore = defineStore("auth", () => {
// State
const user = ref<User | null>(null);
const token = ref<string | null>(localStorage.getItem("authToken"));
const skinUrl = ref<string | null>(null);
const isLoading = ref(false);
const error = ref<string | null>(null);
@@ -16,11 +18,34 @@ export const useAuthStore = defineStore("auth", () => {
const isAuthenticated = computed(() => !!user.value && !!token.value);
// Actions
async function fetchUserProfile() {
if (!user.value) return;
try {
const response = await apiGetUserProfile(user.value.uuid);
if (response.data && response.data.properties) {
const textureProp = response.data.properties.find((p) => p.name === "textures");
if (textureProp) {
// Декодируем Base64-строку и парсим JSON
const textureData = JSON.parse(atob(textureProp.value));
if (textureData.textures?.SKIN?.url) {
skinUrl.value = textureData.textures.SKIN.url;
} else {
skinUrl.value = null; // У пользователя нет скина
}
}
}
} catch (e) {
console.error("Failed to fetch user profile:", e);
skinUrl.value = null;
}
}
function setAuthData(userData: User, authToken: string) {
user.value = userData;
token.value = authToken;
localStorage.setItem("authToken", authToken);
apiClient.defaults.headers.common["Authorization"] = `Bearer ${authToken}`;
fetchUserProfile();
}
async function handleLogin(credentials: LoginRequest) {
@@ -61,15 +86,24 @@ export const useAuthStore = defineStore("auth", () => {
function handleLogout() {
user.value = null;
token.value = null;
skinUrl.value = null;
localStorage.removeItem("authToken");
delete apiClient.defaults.headers.common["Authorization"];
router.push({ name: "login" });
}
async function checkAuth() {
if (token.value) {
if (user.value) {
await fetchUserProfile();
}
}
}
return {
// State
user,
token,
skinUrl,
isLoading,
error,
// Getters
@@ -78,5 +112,7 @@ export const useAuthStore = defineStore("auth", () => {
handleLogin,
handleRegister,
handleLogout,
fetchUserProfile,
checkAuth,
};
});

View File

@@ -26,3 +26,15 @@ export interface User {
created_at: string;
updated_at: string;
}
export interface ProfileProperty {
name: string;
value: string; // Base64-encoded JSON
signature: string;
}
export interface SessionProfileResponse {
id: string;
name: string;
properties: ProfileProperty[];
}

View File

@@ -71,23 +71,49 @@ const onSkinUpload = async () => {
if (skinViewer) {
skinViewer.loadSkin(URL.createObjectURL(selectedFile.value));
}
await authStore.fetchUserProfile();
} catch (e: any) {
uploadError.value = e.response?.data || "Ошибка при загрузке файла.";
} finally {
isLoading.value = false;
}
};
// --- Логика для 3D-вьювера ---
const skinCanvas = ref<HTMLCanvasElement | null>(null);
let skinViewer: SkinViewer | null = null;
// Инициализация 3D-вьювера при монтировании компонента
onMounted(() => {
if (skinCanvas.value) {
const setupSkinViewer = () => {
if (skinCanvas.value && !skinViewer) {
skinViewer = new SkinViewer({
canvas: skinCanvas.value,
width: 300,
height: 400,
skin: "/default_skin.png",
});
}
};
// Следим за изменением URL скина в сторе и обновляем вьювер
watch(
() => authStore.skinUrl,
(newUrl) => {
if (skinViewer) {
if (newUrl) {
skinViewer.loadSkin(newUrl);
} else {
// Если у пользователя нет скина, показываем скин по умолчанию
skinViewer.loadSkin("/default_skin.png");
}
}
},
{ immediate: true },
);
// Инициализация 3D-вьювера при монтировании компонента
onMounted(() => {
setupSkinViewer();
if (!authStore.skinUrl && authStore.isAuthenticated) {
authStore.fetchUserProfile();
}
});
// Очистка ресурсов при размонтировании