add inventory and change cases
This commit is contained in:
@ -1,4 +1,7 @@
|
||||
import { Box, Typography, Button, Grid, Snackbar, Alert } from '@mui/material';
|
||||
import {
|
||||
Box, Typography, Button, Grid,
|
||||
FormControl, Select, MenuItem, InputLabel
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Cape,
|
||||
fetchCapes,
|
||||
@ -30,6 +33,7 @@ import { playBuySound, primeSounds } from '../utils/sounds';
|
||||
import CustomNotification from '../components/Notifications/CustomNotification';
|
||||
import type { NotificationPosition } from '../components/Notifications/CustomNotification';
|
||||
import { isNotificationsEnabled, getNotifPositionFromSettings } from '../utils/notifications';
|
||||
import { translateServer } from '../utils/serverTranslator';
|
||||
|
||||
function getRarityByWeight(
|
||||
weight?: number,
|
||||
@ -66,6 +70,8 @@ export default function Shop() {
|
||||
|
||||
const [playerSkinUrl, setPlayerSkinUrl] = useState<string>('');
|
||||
|
||||
const [selectedCaseServerIp, setSelectedCaseServerIp] = useState<string>('');
|
||||
|
||||
// Уведомления
|
||||
|
||||
const [notifOpen, setNotifOpen] = useState(false);
|
||||
@ -345,6 +351,28 @@ export default function Shop() {
|
||||
});
|
||||
};
|
||||
|
||||
const caseServers = Array.from(
|
||||
new Set(
|
||||
(cases || [])
|
||||
.flatMap((c) => c.server_ips || [])
|
||||
.filter(Boolean),
|
||||
),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (caseServers.length > 0) {
|
||||
// если игрок онлайн — по умолчанию его сервер, если он есть в кейсах
|
||||
const preferred =
|
||||
playerServer?.ip && caseServers.includes(playerServer.ip)
|
||||
? playerServer.ip
|
||||
: caseServers[0];
|
||||
|
||||
setSelectedCaseServerIp(preferred);
|
||||
}
|
||||
}, [caseServers.length, playerServer?.ip]);
|
||||
|
||||
const filteredCases = cases;
|
||||
|
||||
// Фильтруем плащи, которые уже куплены пользователем
|
||||
const availableCapes = storeCapes.filter(
|
||||
(storeCape) =>
|
||||
@ -352,62 +380,72 @@ export default function Shop() {
|
||||
);
|
||||
|
||||
const handleOpenCase = async (caseData: Case) => {
|
||||
if (!username) {
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Не найдено имя игрока. Авторизуйтесь в лаунчере!');
|
||||
setNotifSeverity('error');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
}
|
||||
if (!username) {
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Не найдено имя игрока. Авторизуйтесь в лаунчере!');
|
||||
setNotifSeverity('error');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isOnline || !playerServer) {
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Для открытия кейсов необходимо находиться на сервере в игре!');
|
||||
setNotifSeverity('error');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
return;
|
||||
}
|
||||
if (!selectedCaseServerIp) {
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Выберите сервер для открытия кейса!');
|
||||
setNotifSeverity('warning');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isOpening) return;
|
||||
const allowedIps = caseData.server_ips || [];
|
||||
if (allowedIps.length > 0 && !allowedIps.includes(selectedCaseServerIp)) {
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg(
|
||||
`Этот кейс доступен на: ${allowedIps
|
||||
.map((ip) => translateServer(`Server ${ip}`))
|
||||
.join(', ')}`,
|
||||
);
|
||||
setNotifSeverity('warning');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsOpening(true);
|
||||
if (isOpening) return;
|
||||
|
||||
// 1. получаем полный кейс
|
||||
const fullCase = await fetchCase(caseData.id);
|
||||
const caseItems: CaseItem[] = fullCase.items || [];
|
||||
setSelectedCase(fullCase);
|
||||
try {
|
||||
setIsOpening(true);
|
||||
|
||||
// 2. открываем кейс на бэке
|
||||
const result = await openCase(fullCase.id, username, playerServer.id);
|
||||
const fullCase = await fetchCase(caseData.id);
|
||||
const caseItems: CaseItem[] = fullCase.items || [];
|
||||
setSelectedCase(fullCase);
|
||||
|
||||
// 3. сохраняем данные для рулетки
|
||||
setRouletteCaseItems(caseItems);
|
||||
setRouletteReward(result.reward);
|
||||
setRouletteOpen(true);
|
||||
playBuySound();
|
||||
// ✅ открываем на выбранном сервере (даже если игрок не на сервере)
|
||||
const result = await openCase(fullCase.id, username, selectedCaseServerIp);
|
||||
|
||||
setIsOpening(false);
|
||||
setRouletteCaseItems(caseItems);
|
||||
setRouletteReward(result.reward);
|
||||
setRouletteOpen(true);
|
||||
playBuySound();
|
||||
|
||||
// 4. уведомление
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Кейс открыт!');
|
||||
setNotifSeverity('success');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
} catch (error) {
|
||||
console.error('Ошибка при открытии кейса:', error);
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Кейс открыт!');
|
||||
setNotifSeverity('success');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
} catch (error) {
|
||||
console.error('Ошибка при открытии кейса:', error);
|
||||
|
||||
setIsOpening(false);
|
||||
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Ошибка при открытии кейса!');
|
||||
setNotifSeverity('error');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
}
|
||||
};
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg(String(error instanceof Error ? error.message : 'Ошибка при открытии кейса!'));
|
||||
setNotifSeverity('error');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
} finally {
|
||||
setIsOpening(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCloseNotification = () => {
|
||||
setNotification((prev) => ({ ...prev, open: false }));
|
||||
@ -570,8 +608,6 @@ export default function Shop() {
|
||||
>
|
||||
Кейсы
|
||||
</Typography>
|
||||
|
||||
{!isOnline && (
|
||||
<Button
|
||||
disableRipple
|
||||
disableFocusRipple
|
||||
@ -599,22 +635,76 @@ export default function Shop() {
|
||||
>
|
||||
Обновить
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{caseServers.length > 0 && (
|
||||
<FormControl size="small" sx={{ minWidth: 220 }}>
|
||||
<InputLabel id="cases-server-label" sx={{ fontFamily: 'Benzin-Bold', color: 'rgba(255,255,255,0.75)' }}>
|
||||
Сервер
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId="cases-server-label"
|
||||
label="Сервер"
|
||||
value={selectedCaseServerIp}
|
||||
onChange={(e) => setSelectedCaseServerIp(String(e.target.value))}
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
sx: {
|
||||
bgcolor: 'rgba(10,10,20,0.96)',
|
||||
border: '1px solid rgba(255,255,255,0.10)',
|
||||
borderRadius: '1vw',
|
||||
backdropFilter: 'blur(14px)',
|
||||
'& .MuiMenuItem-root': {
|
||||
color: 'rgba(255,255,255,0.9)',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
},
|
||||
'& .MuiMenuItem-root.Mui-selected': {
|
||||
backgroundColor: 'rgba(242,113,33,0.16)',
|
||||
},
|
||||
'& .MuiMenuItem-root:hover': {
|
||||
backgroundColor: 'rgba(233,64,205,0.14)',
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
borderRadius: '999px',
|
||||
bgcolor: 'rgba(255,255,255,0.04)',
|
||||
color: 'rgba(255,255,255,0.92)',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
'& .MuiSelect-select': {
|
||||
py: '0.9vw',
|
||||
px: '1.2vw',
|
||||
},
|
||||
'& .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: 'rgba(255,255,255,0.14)',
|
||||
},
|
||||
'&:hover .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: 'rgba(242,113,33,0.55)',
|
||||
},
|
||||
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: 'rgba(233,64,205,0.65)',
|
||||
borderWidth: '2px',
|
||||
},
|
||||
'& .MuiSelect-icon': {
|
||||
color: 'rgba(255,255,255,0.75)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{caseServers.map((ip) => (
|
||||
<MenuItem key={ip} value={ip}>
|
||||
{translateServer(`Server ${ip}`)}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{!isOnline ? (
|
||||
<Typography variant="body1" color="error" sx={{ mb: 2 }}>
|
||||
Для открытия кейсов вам необходимо находиться на одном из
|
||||
серверов игры. Зайдите в игру и нажмите кнопку «Обновить».
|
||||
</Typography>
|
||||
) : casesLoading ? (
|
||||
<FullScreenLoader
|
||||
fullScreen={false}
|
||||
message="Загрузка кейсов..."
|
||||
/>
|
||||
{casesLoading ? (
|
||||
<FullScreenLoader fullScreen={false} message="Загрузка кейсов..." />
|
||||
) : cases.length > 0 ? (
|
||||
<Grid container spacing={2} sx={{ mb: 4 }}>
|
||||
{cases.map((c) => (
|
||||
{filteredCases.map((c) => (
|
||||
<Grid item xs={12} sm={6} md={4} lg={3} key={c.id}>
|
||||
<ShopItem
|
||||
type="case"
|
||||
@ -625,7 +715,7 @@ export default function Shop() {
|
||||
price={c.price}
|
||||
itemsCount={c.items_count}
|
||||
isOpening={isOpening && selectedCase?.id === c.id}
|
||||
disabled={!isOnline || isOpening}
|
||||
disabled={isOpening || !selectedCaseServerIp}
|
||||
onClick={() => handleOpenCase(c)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
Reference in New Issue
Block a user