mnoga che sdelal
This commit is contained in:
@ -134,7 +134,7 @@ const AppLayout = () => {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: location.pathname === '/profile' || location.pathname.startsWith('/launch')
|
||||
justifyContent: location.pathname === '/profile' || location.pathname.startsWith('/launch') || location.pathname === '/login' || '/registration'
|
||||
? 'center'
|
||||
: 'flex-start',
|
||||
overflowX: 'hidden',
|
||||
|
||||
@ -3,6 +3,7 @@ import { Box, Typography } from '@mui/material';
|
||||
import CustomTooltip from './Notifications/CustomTooltip';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { fetchCoins } from '../api';
|
||||
import type { SxProps, Theme } from '@mui/material/styles';
|
||||
|
||||
interface CoinsDisplayProps {
|
||||
// Основные пропсы
|
||||
@ -23,6 +24,8 @@ interface CoinsDisplayProps {
|
||||
// Стилизация
|
||||
backgroundColor?: string;
|
||||
textColor?: string;
|
||||
|
||||
sx?: SxProps<Theme>;
|
||||
}
|
||||
|
||||
export default function CoinsDisplay({
|
||||
@ -44,6 +47,8 @@ export default function CoinsDisplay({
|
||||
// Стилизация
|
||||
backgroundColor = 'rgba(0, 0, 0, 0.2)',
|
||||
textColor = 'white',
|
||||
|
||||
sx,
|
||||
}: CoinsDisplayProps) {
|
||||
const [coins, setCoins] = useState<number>(externalValue || 0);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
@ -142,6 +147,8 @@ export default function CoinsDisplay({
|
||||
cursor: showTooltip ? 'help' : 'default',
|
||||
opacity: isLoading ? 0.7 : 1,
|
||||
transition: 'opacity 0.2s ease',
|
||||
|
||||
...sx,
|
||||
}}
|
||||
onClick={username ? handleRefresh : undefined}
|
||||
title={username ? 'Нажмите для обновления' : undefined}
|
||||
|
||||
@ -1,28 +1,33 @@
|
||||
// src/renderer/components/HeadAvatar.tsx
|
||||
import { useEffect, useRef } from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
|
||||
interface HeadAvatarProps {
|
||||
skinUrl?: string;
|
||||
size?: number; // финальный размер головы, px
|
||||
size?: number;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const DEFAULT_SKIN =
|
||||
'https://static.planetminecraft.com/files/resource_media/skin/original-steve-15053860.png';
|
||||
|
||||
export const HeadAvatar: React.FC<HeadAvatarProps> = ({
|
||||
skinUrl,
|
||||
size = 24,
|
||||
style,
|
||||
...canvasProps
|
||||
}) => {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!skinUrl || !canvasRef.current) return;
|
||||
const finalSkinUrl = skinUrl?.trim() ? skinUrl : DEFAULT_SKIN;
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'anonymous'; // на всякий случай, если CDN
|
||||
img.src = skinUrl;
|
||||
img.crossOrigin = 'anonymous';
|
||||
img.src = finalSkinUrl;
|
||||
|
||||
img.onload = () => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
@ -30,26 +35,12 @@ export const HeadAvatar: React.FC<HeadAvatarProps> = ({
|
||||
canvas.height = size;
|
||||
|
||||
ctx.clearRect(0, 0, size, size);
|
||||
|
||||
// Координаты головы в стандартном скине 64x64:
|
||||
// База головы: (8, 8, 8, 8)
|
||||
// Слой шляпы/маски: (40, 8, 8, 8)
|
||||
|
||||
// Рисуем основную голову
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
ctx.drawImage(
|
||||
img,
|
||||
8, // sx
|
||||
8, // sy
|
||||
8, // sWidth
|
||||
8, // sHeight
|
||||
0, // dx
|
||||
0, // dy
|
||||
size, // dWidth
|
||||
size, // dHeight
|
||||
);
|
||||
|
||||
// Рисуем слой шляпы поверх (если есть)
|
||||
// База головы: (8, 8, 8, 8)
|
||||
ctx.drawImage(img, 8, 8, 8, 8, 0, 0, size, size);
|
||||
|
||||
// Слой шляпы: (40, 8, 8, 8)
|
||||
ctx.drawImage(img, 40, 8, 8, 8, 0, 0, size, size);
|
||||
};
|
||||
|
||||
@ -61,11 +52,13 @@ export const HeadAvatar: React.FC<HeadAvatarProps> = ({
|
||||
return (
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
{...canvasProps}
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
borderRadius: 4,
|
||||
imageRendering: 'pixelated',
|
||||
...style, // 👈 даём переопределять снаружи
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -4,6 +4,7 @@ import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
|
||||
import VisibilityIcon from '@mui/icons-material/Visibility';
|
||||
import GradientTextField from '../GradientTextField';
|
||||
import GradientVisibilityToggleIcon from '../../assets/Icons/GradientVisibilityToggleIcon'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
interface AuthFormProps {
|
||||
config: {
|
||||
@ -16,6 +17,7 @@ interface AuthFormProps {
|
||||
|
||||
const AuthForm = ({ config, handleInputChange, onLogin }: AuthFormProps) => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@ -92,6 +94,33 @@ const AuthForm = ({ config, handleInputChange, onLogin }: AuthFormProps) => {
|
||||
}}>
|
||||
Войти
|
||||
</Button>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
onClick={() => navigate('/registration')}
|
||||
sx={{
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '1vw',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.08em',
|
||||
cursor: 'pointer',
|
||||
backgroundImage:
|
||||
'linear-gradient(71deg, #F27121 0%, #E940CD 60%, #8A2387 100%)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
textShadow: '0 0 15px rgba(0,0,0,0.9)',
|
||||
'&:hover': {
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Зарегистрироваться
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useMemo } from 'react';
|
||||
import { useMemo, useEffect, useState } from 'react';
|
||||
|
||||
interface HeaderConfig {
|
||||
title: string;
|
||||
@ -10,6 +10,19 @@ interface HeaderConfig {
|
||||
|
||||
export default function PageHeader() {
|
||||
const location = useLocation();
|
||||
const [isAuthed, setIsAuthed] = useState(false);
|
||||
|
||||
const isLaunchPage = location.pathname.startsWith('/launch');
|
||||
|
||||
useEffect(() => {
|
||||
const saved = localStorage.getItem('launcher_config');
|
||||
try {
|
||||
const cfg = saved ? JSON.parse(saved) : null;
|
||||
setIsAuthed(Boolean(cfg?.accessToken)); // или cfg?.uuid/username
|
||||
} catch {
|
||||
setIsAuthed(false);
|
||||
}
|
||||
}, [location.pathname]);
|
||||
|
||||
const headerConfig: HeaderConfig | null = useMemo(() => {
|
||||
const path = location.pathname;
|
||||
@ -34,7 +47,7 @@ export default function PageHeader() {
|
||||
|
||||
if (path === '/') {
|
||||
return {
|
||||
title: 'Выбор версию клиента',
|
||||
title: 'Выбор версии клиента',
|
||||
subtitle: 'Выберите установленную версию или добавьте новую сборку',
|
||||
};
|
||||
}
|
||||
@ -49,39 +62,31 @@ export default function PageHeader() {
|
||||
if (path.startsWith('/dailyquests')) {
|
||||
return {
|
||||
title: 'Ежедневные задания',
|
||||
subtitle: 'Выполняйте ежедневные задания разной сложности и получайте награды!',
|
||||
};
|
||||
}
|
||||
|
||||
if (path.startsWith('/profile')) {
|
||||
return {
|
||||
title: 'Профиль пользователя',
|
||||
subtitle: 'Профиль пользователя для личных настроек',
|
||||
subtitle:
|
||||
'Выполняйте ежедневные задания разной сложности и получайте награды!',
|
||||
};
|
||||
}
|
||||
|
||||
if (path.startsWith('/shop')) {
|
||||
return {
|
||||
title: 'Внутриигровой магазин',
|
||||
subtitle: 'Тратьте свою уникалькую, виртуальную валюту - Попы!',
|
||||
subtitle: 'Тратьте свою уникальную виртуальную валюту — Попы!',
|
||||
};
|
||||
}
|
||||
|
||||
if (path.startsWith('/marketplace')) {
|
||||
return {
|
||||
title: 'Маркетплейс',
|
||||
subtitle: 'Покупайте или продавайте - торговая площадка между игроками',
|
||||
subtitle: 'Покупайте или продавайте — торговая площадка между игроками',
|
||||
};
|
||||
}
|
||||
|
||||
// Дефолт на всякий случай
|
||||
return {
|
||||
title: 'test',
|
||||
subtitle: 'test',
|
||||
};
|
||||
// Дефолт
|
||||
return { title: 'test', subtitle: 'test' };
|
||||
}, [location.pathname]);
|
||||
|
||||
if (!headerConfig || headerConfig.hidden) {
|
||||
// ✅ один общий guard — тут и “hidden”, и “не авторизован”, и launch
|
||||
if (!headerConfig || headerConfig.hidden || !isAuthed || isLaunchPage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -115,11 +120,11 @@ export default function PageHeader() {
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{
|
||||
mt: 0.5,
|
||||
color: 'rgba(255,255,255,1)',
|
||||
}}
|
||||
variant="body2"
|
||||
sx={{
|
||||
mt: 0.5,
|
||||
color: 'rgba(255,255,255,1)',
|
||||
}}
|
||||
>
|
||||
{headerConfig.subtitle}
|
||||
</Typography>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Box, Button, Tab, Tabs, Typography } from '@mui/material';
|
||||
import { Box, Button, Tab, Tabs, Typography, Menu, MenuItem, Divider } from '@mui/material';
|
||||
import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded';
|
||||
@ -7,6 +7,11 @@ import { Tooltip } from '@mui/material';
|
||||
import { fetchCoins } from '../api';
|
||||
import CustomTooltip from './Notifications/CustomTooltip';
|
||||
import CoinsDisplay from './CoinsDisplay';
|
||||
import { HeadAvatar } from './HeadAvatar';
|
||||
import { fetchPlayer } from './../api'
|
||||
import CalendarMonthIcon from '@mui/icons-material/CalendarMonth';
|
||||
import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
|
||||
import PersonIcon from '@mui/icons-material/Person';
|
||||
declare global {
|
||||
interface Window {
|
||||
electron: {
|
||||
@ -29,14 +34,38 @@ export default function TopBar({ onRegister, username }: TopBarProps) {
|
||||
// Получаем текущий путь
|
||||
const location = useLocation();
|
||||
const isLoginPage = location.pathname === '/login';
|
||||
const [isAuthed, setIsAuthed] = useState(false);
|
||||
const isLaunchPage = location.pathname.startsWith('/launch');
|
||||
const isVersionsExplorerPage = location.pathname.startsWith('/');
|
||||
const isRegistrationPage = location.pathname === '/registration';
|
||||
const navigate = useNavigate();
|
||||
const [coins, setCoins] = useState<number>(0);
|
||||
const [value, setValue] = useState(1);
|
||||
const [activePage, setActivePage] = useState('versions');
|
||||
const tabsWrapperRef = useRef<HTMLDivElement | null>(null);
|
||||
const [skinUrl, setSkinUrl] = useState<string>('');
|
||||
const [avatarAnchorEl, setAvatarAnchorEl] =
|
||||
useState<null | HTMLElement>(null);
|
||||
const [menuOpen, setMenuOpen] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
const saved = localStorage.getItem('launcher_config');
|
||||
try {
|
||||
const cfg = saved ? JSON.parse(saved) : null;
|
||||
setIsAuthed(Boolean(cfg?.accessToken)); // или cfg?.uuid/username — как у тебя принято
|
||||
} catch {
|
||||
setIsAuthed(false);
|
||||
}
|
||||
}, [location.pathname]); // можно и без dependency, но так надёжнее при logout/login
|
||||
|
||||
const avatarMenuOpen = Boolean(avatarAnchorEl);
|
||||
|
||||
const handleAvatarClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAvatarAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleAvatarMenuClose = () => {
|
||||
setAvatarAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
|
||||
setValue(newValue);
|
||||
@ -147,6 +176,19 @@ export default function TopBar({ onRegister, username }: TopBarProps) {
|
||||
navigate('/login');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const savedConfig = localStorage.getItem('launcher_config');
|
||||
if (!savedConfig) return;
|
||||
|
||||
const config = JSON.parse(savedConfig);
|
||||
const uuid = config.uuid;
|
||||
if (!uuid) return;
|
||||
|
||||
fetchPlayer(uuid)
|
||||
.then((player) => setSkinUrl(player.skin_url))
|
||||
.catch((e) => console.error('Не удалось получить скин:', e));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@ -197,7 +239,7 @@ export default function TopBar({ onRegister, username }: TopBarProps) {
|
||||
<ArrowBackRoundedIcon />
|
||||
</Button>
|
||||
)}
|
||||
{!isLaunchPage && !isRegistrationPage && !isLoginPage && (
|
||||
{isAuthed && !isLaunchPage && (
|
||||
<Box
|
||||
ref={tabsWrapperRef}
|
||||
onWheel={handleTabsWheel}
|
||||
@ -264,25 +306,6 @@ export default function TopBar({ onRegister, username }: TopBarProps) {
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
label="Профиль"
|
||||
disableRipple={true}
|
||||
onClick={() => {
|
||||
setActivePage('profile');
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '0.7em',
|
||||
'&.Mui-selected': {
|
||||
color: 'rgba(255, 77, 77, 1)',
|
||||
},
|
||||
'&:hover': {
|
||||
color: 'rgb(177, 52, 52)',
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
label="Магазин"
|
||||
disableRipple={true}
|
||||
@ -357,32 +380,17 @@ export default function TopBar({ onRegister, username }: TopBarProps) {
|
||||
}}
|
||||
>
|
||||
{!isLoginPage && !isRegistrationPage && username && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => logout()}
|
||||
sx={{
|
||||
width: '8em',
|
||||
height: '3em',
|
||||
borderRadius: '2.5vw',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '0.9em',
|
||||
background:
|
||||
'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
transition: 'transform 0.3s ease',
|
||||
'&:hover': {
|
||||
background:
|
||||
'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)',
|
||||
transform: 'scale(1.01)',
|
||||
boxShadow: '0 4px 15px rgba(242, 113, 33, 0.4)',
|
||||
},
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: '1vw' }}>
|
||||
<HeadAvatar
|
||||
skinUrl={skinUrl}
|
||||
size={44}
|
||||
style={{
|
||||
borderRadius: '3vw',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
Выйти
|
||||
</Button>
|
||||
onClick={handleAvatarClick}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{/* Кнопка регистрации, если на странице логина */}
|
||||
{!isLoginPage && !isRegistrationPage && username && (
|
||||
@ -393,37 +401,6 @@ export default function TopBar({ onRegister, username }: TopBarProps) {
|
||||
showTooltip={true}
|
||||
/>
|
||||
)}
|
||||
{isLoginPage && (
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => navigate('/registration')}
|
||||
sx={{
|
||||
width: '13em',
|
||||
height: '3em',
|
||||
borderRadius: '2.5vw',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '0.9em',
|
||||
background:
|
||||
'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
transition: 'all 0.3s ease',
|
||||
'&:hover': {
|
||||
background:
|
||||
'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)',
|
||||
transform: 'scale(1.01)',
|
||||
boxShadow: '0 4px 15px rgba(242, 113, 33, 0.4)',
|
||||
},
|
||||
'&:active': {
|
||||
color: 'transparent',
|
||||
},
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',
|
||||
userSelect: 'none',
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontSize: '1em', color: 'white' }}>Регистрация</Typography>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Кнопки управления окном */}
|
||||
<Button
|
||||
@ -465,6 +442,165 @@ export default function TopBar({ onRegister, username }: TopBarProps) {
|
||||
<CloseRoundedIcon sx={{ color: 'white' }} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Menu
|
||||
anchorEl={avatarAnchorEl}
|
||||
open={avatarMenuOpen}
|
||||
onClose={handleAvatarMenuClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
mt: '0.5vw',
|
||||
borderRadius: '1vw',
|
||||
minWidth: '16vw',
|
||||
bgcolor: 'rgba(0,0,0,0.92)',
|
||||
color: 'white',
|
||||
boxShadow: '0 0 20px rgba(0,0,0,0.6)',
|
||||
border: '1px solid rgba(255,77,77,0.35)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{/* ===== 1 строка: аватар + ник + валюта ===== */}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'auto 1fr',
|
||||
gap: '1.5vw',
|
||||
alignItems: 'center',
|
||||
px: '2vw',
|
||||
py: '0.8vw',
|
||||
}}
|
||||
>
|
||||
<HeadAvatar
|
||||
skinUrl={skinUrl}
|
||||
size={40}
|
||||
style={{ borderRadius: '3vw' }}
|
||||
/>
|
||||
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '2vw',
|
||||
}}
|
||||
>
|
||||
{username || 'Игрок'}
|
||||
</Typography>
|
||||
|
||||
<CoinsDisplay
|
||||
username={username}
|
||||
size="medium"
|
||||
autoUpdate={true}
|
||||
showTooltip={true}
|
||||
sx={{
|
||||
border: 'none',
|
||||
padding: '0vw',
|
||||
}}
|
||||
backgroundColor={'rgba(0, 0, 0, 0)'}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Divider sx={{ my: '0.4vw', borderColor: 'rgba(255,255,255,0.08)' }} />
|
||||
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleAvatarMenuClose();
|
||||
navigate('/profile');
|
||||
}}
|
||||
sx={{
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '1.5vw',
|
||||
gap: '0.5vw',
|
||||
py: '0.7vw',
|
||||
'&:hover': {
|
||||
bgcolor: 'rgba(255,77,77,0.15)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<PersonIcon sx={{ fontSize: '2vw' }}/> Профиль
|
||||
</MenuItem>
|
||||
|
||||
{/* ===== 2 строка: ежедневные задания ===== */}
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleAvatarMenuClose();
|
||||
navigate('/dailyquests');
|
||||
}}
|
||||
sx={{
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '1.5vw',
|
||||
gap: '0.5vw',
|
||||
py: '0.7vw',
|
||||
'&:hover': {
|
||||
bgcolor: 'rgba(255,77,77,0.15)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CalendarMonthIcon sx={{ fontSize: '2vw' }} /> Ежедневные задания
|
||||
</MenuItem>
|
||||
|
||||
{/* ===== 3 строка: ежедневная награда ===== */}
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleAvatarMenuClose();
|
||||
navigate('/daily');
|
||||
}}
|
||||
sx={{
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '1.5vw',
|
||||
gap: '0.5vw',
|
||||
py: '0.7vw',
|
||||
'&:hover': {
|
||||
bgcolor: 'rgba(255,77,77,0.15)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<EmojiEventsIcon sx={{ fontSize: '2vw' }} /> Ежедневная награда
|
||||
</MenuItem>
|
||||
|
||||
<Divider sx={{ my: '0.4vw', borderColor: 'rgba(255,255,255,0.08)' }} />
|
||||
{!isLoginPage && !isRegistrationPage && username && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
logout();
|
||||
setMenuOpen(false);
|
||||
}}
|
||||
sx={{
|
||||
width: '8vw',
|
||||
height: '3vw',
|
||||
borderRadius: '2.5vw',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '1.2vw',
|
||||
background:
|
||||
'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
transition: 'transform 0.3s ease',
|
||||
'&:hover': {
|
||||
background:
|
||||
'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)',
|
||||
transform: 'scale(1.01)',
|
||||
boxShadow: '0 4px 15px rgba(242, 113, 33, 0.4)',
|
||||
},
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',
|
||||
m: '0 0 0 18vw'
|
||||
}}
|
||||
>
|
||||
Выйти
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* ↓↓↓ дальше ты сам добавишь пункты ↓↓↓ */}
|
||||
</Menu>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ import CustomNotification from '../components/Notifications/CustomNotification';
|
||||
import type { NotificationPosition } from '../components/Notifications/CustomNotification';
|
||||
import * as React from 'react';
|
||||
import { playBuySound } from '../utils/sounds';
|
||||
import { translateServer } from '../utils/serverTranslator'
|
||||
|
||||
interface TabPanelProps {
|
||||
children?: React.ReactNode;
|
||||
@ -66,19 +67,6 @@ export default function Marketplace() {
|
||||
horizontal: 'center',
|
||||
});
|
||||
|
||||
const translateServer = (server: Server) => {
|
||||
switch (server.name) {
|
||||
case 'Server minecraft.hub.popa-popa.ru':
|
||||
return 'Хаб';
|
||||
case 'Server survival.hub.popa-popa.ru':
|
||||
return 'Выживание';
|
||||
case 'Server minecraft.minigames.popa-popa.ru':
|
||||
return 'Миниигры';
|
||||
default:
|
||||
return server.name;
|
||||
}
|
||||
};
|
||||
|
||||
// Функция для проверки онлайн-статуса игрока и определения сервера
|
||||
const checkPlayerStatus = async () => {
|
||||
if (!username) return;
|
||||
|
||||
@ -62,14 +62,6 @@ export default function Profile() {
|
||||
horizontal: 'right',
|
||||
});
|
||||
|
||||
const navigateDaily = () => {
|
||||
navigate('/daily');
|
||||
};
|
||||
|
||||
const navigateDailyQuests = () => {
|
||||
navigate('/dailyquests');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const savedConfig = localStorage.getItem('launcher_config');
|
||||
if (savedConfig) {
|
||||
@ -501,38 +493,6 @@ export default function Profile() {
|
||||
</Box>
|
||||
</Box>
|
||||
<OnlinePlayersPanel currentUsername={username} />
|
||||
<Button
|
||||
variant="contained"
|
||||
fullWidth
|
||||
onClick={navigateDaily}
|
||||
sx={{
|
||||
mt: 1,
|
||||
transition: 'transform 0.3s ease',
|
||||
background:
|
||||
'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
borderRadius: '2.5vw',
|
||||
'&:hover': { transform: 'scale(1.03)' },
|
||||
}}
|
||||
>
|
||||
Ежедневные награды
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
fullWidth
|
||||
onClick={navigateDailyQuests}
|
||||
sx={{
|
||||
mt: 1,
|
||||
transition: 'transform 0.3s ease',
|
||||
background:
|
||||
'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
borderRadius: '2.5vw',
|
||||
'&:hover': { transform: 'scale(1.03)' },
|
||||
}}
|
||||
>
|
||||
Ежедневные квесты
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -1,21 +1,15 @@
|
||||
// src/renderer/utils/serverTranslator.ts
|
||||
import { Server } from '../api';
|
||||
|
||||
type ServerLike = Pick<Server, 'name'> | { name: string };
|
||||
|
||||
export const translateServer = (
|
||||
server: ServerLike | null | undefined,
|
||||
): string => {
|
||||
if (!server?.name) return '';
|
||||
|
||||
export function translateServer(server: Server): string {
|
||||
switch (server.name) {
|
||||
case 'Server minecraft.hub.popa-popa.ru':
|
||||
return 'Хаб';
|
||||
case 'Server survival.hub.popa-popa.ru':
|
||||
case 'Server minecraft.survival.popa-popa.ru':
|
||||
return 'Выживание';
|
||||
case 'Server minecraft.minigames.popa-popa.ru':
|
||||
return 'Миниигры';
|
||||
default:
|
||||
return server.name;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user