From 26f601635bea47b08493f06fe244e05fa2c6d8a3 Mon Sep 17 00:00:00 2001 From: DIKER0K Date: Fri, 18 Jul 2025 20:14:44 +0500 Subject: [PATCH] add: capes switch in user profile --- src/renderer/api.ts | 68 ++++++ src/renderer/pages/Profile.tsx | 364 +++++++++++++++++++++++---------- 2 files changed, 319 insertions(+), 113 deletions(-) diff --git a/src/renderer/api.ts b/src/renderer/api.ts index e8a98b8..dd698bd 100644 --- a/src/renderer/api.ts +++ b/src/renderer/api.ts @@ -19,6 +19,17 @@ export interface CoinsResponse { }; } +export interface Cape { + cape_id: string; + cape_name: string; + cape_description: string; + image_url: string; + purchased_at: string; + is_active: boolean; +} + +export type CapesResponse = Cape[]; + export interface ApiError { message: string; details?: string; @@ -55,6 +66,63 @@ export async function fetchCoins(username: string): Promise { } } +export async function fetchCapes(username: string): Promise { + try { + const response = await fetch( + `${API_BASE_URL}/store/user/${username}/capes`, + ); + if (!response.ok) { + return []; // Если плащи не найдены, возвращаем пустой массив + } + return await response.json(); + } catch (error) { + console.error('API ошибка:', error); + return []; // В случае ошибки возвращаем пустой массив + } +} + +export async function activateCape( + username: string, + cape_id: string, +): Promise { + const response = await fetch( + `${API_BASE_URL}/store/user/${username}/capes/activate/${cape_id}`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + cape_id: cape_id, + }), + }, + ); + if (!response.ok) { + throw new Error('Не удалось активировать плащ'); + } +} + +export async function deactivateCape( + username: string, + cape_id: string, +): Promise { + const response = await fetch( + `${API_BASE_URL}/store/user/${username}/capes/deactivate/${cape_id}`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + cape_id: cape_id, + }), + }, + ); + if (!response.ok) { + throw new Error('Не удалось деактивировать плащ'); + } +} + // Загрузка скина export async function uploadSkin( username: string, diff --git a/src/renderer/pages/Profile.tsx b/src/renderer/pages/Profile.tsx index acf6fff..37ceb1d 100644 --- a/src/renderer/pages/Profile.tsx +++ b/src/renderer/pages/Profile.tsx @@ -1,6 +1,13 @@ import { useEffect, useRef, useState } from 'react'; import SkinViewer from '../components/SkinViewer'; -import { fetchPlayer, uploadSkin } from '../api'; +import { + fetchPlayer, + uploadSkin, + fetchCapes, + Cape, + activateCape, + deactivateCape, +} from '../api'; import { Box, @@ -13,6 +20,11 @@ import { MenuItem, Alert, CircularProgress, + Card, + CardContent, + CardMedia, + Tooltip, + CardActions, } from '@mui/material'; export default function Profile() { @@ -28,6 +40,9 @@ export default function Profile() { >('idle'); const [statusMessage, setStatusMessage] = useState(''); const [isDragOver, setIsDragOver] = useState(false); + const [capes, setCapes] = useState([]); + const [uuid, setUuid] = useState(''); + const [loading, setLoading] = useState(false); useEffect(() => { const savedConfig = localStorage.getItem('launcher_config'); @@ -36,15 +51,19 @@ export default function Profile() { if (config.uuid) { loadPlayerData(config.uuid); setUsername(config.username || ''); + loadCapesData(config.username || ''); + setUuid(config.uuid || ''); } } }, []); const loadPlayerData = async (uuid: string) => { try { + setLoading(true); const player = await fetchPlayer(uuid); setSkin(player.skin_url); setCape(player.cloak_url); + setLoading(false); } catch (error) { console.error('Ошибка при получении данных игрока:', error); setSkin(''); @@ -52,6 +71,18 @@ export default function Profile() { } }; + const loadCapesData = async (username: string) => { + try { + setLoading(true); + const capesData = await fetchCapes(username); + setCapes(capesData); + setLoading(false); + } catch (error) { + console.error('Ошибка при получении плащей:', error); + setCapes([]); + } + }; + // Обработка перетаскивания файла const handleFileDrop = (e: React.DragEvent) => { e.preventDefault(); @@ -83,8 +114,24 @@ export default function Profile() { } }; + const handleActivateCape = async (cape_id: string) => { + setLoading(true); + await activateCape(username, cape_id); + await loadCapesData(username); + setLoading(false); + }; + + const handleDeactivateCape = async (cape_id: string) => { + setLoading(true); + await deactivateCape(username, cape_id); + await loadCapesData(username); + await loadPlayerData(uuid); + setLoading(false); + }; + // Отправка запроса на установку скина const handleUploadSkin = async () => { + setLoading(true); if (!skinFile || !username) { setStatusMessage('Необходимо выбрать файл и указать имя пользователя'); setUploadStatus('error'); @@ -111,6 +158,8 @@ export default function Profile() { `Ошибка: ${error instanceof Error ? error.message : 'Не удалось загрузить скин'}`, ); setUploadStatus('error'); + } finally { + setLoading(false); } }; @@ -124,124 +173,213 @@ export default function Profile() { gap: 2, }} > - - {/* Используем переработанный компонент SkinViewer */} - - - - - - Установить скин - - - { - e.preventDefault(); - setIsDragOver(true); - }} - onDragLeave={() => setIsDragOver(false)} - onDrop={handleFileDrop} - onClick={() => fileInputRef.current?.click()} - > - - - {skinFile - ? `Выбран файл: ${skinFile.name}` - : 'Перетащите PNG файл скина или кликните для выбора'} - - - - - Модель скина - - + {/* Используем переработанный компонент SkinViewer */} + + - {uploadStatus === 'error' && ( - - {statusMessage} - - )} + + + { + e.preventDefault(); + setIsDragOver(true); + }} + onDragLeave={() => setIsDragOver(false)} + onDrop={handleFileDrop} + onClick={() => fileInputRef.current?.click()} + > + + + {skinFile + ? `Выбран файл: ${skinFile.name}` + : 'Перетащите PNG файл скина или кликните для выбора'} + + - {uploadStatus === 'success' && ( - - {statusMessage} - - )} + + Модель скина + + - - + {uploadStatus === 'error' && ( + + {statusMessage} + + )} + + {uploadStatus === 'success' && ( + + {statusMessage} + + )} + + + + + Ваши плащи + + {capes.map((cape) => ( + + + + + + {cape.cape_name} + + + {cape.is_active ? ( + + + + ) : ( + + + + )} + + + ))} + + + + + )} ); }