axyenniu daily reward page
This commit is contained in:
@ -11,7 +11,7 @@ import {
|
||||
Box,
|
||||
Chip,
|
||||
} from '@mui/material';
|
||||
import CustomTooltip from './CustomTooltip';
|
||||
import CustomTooltip from './Notifications/CustomTooltip';
|
||||
// Тип для плаща с необязательными полями для обоих вариантов использования
|
||||
export interface CapeCardProps {
|
||||
cape: {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// CoinsDisplay.tsx
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import CustomTooltip from './CustomTooltip';
|
||||
import CustomTooltip from './Notifications/CustomTooltip';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { fetchCoins } from '../api';
|
||||
|
||||
@ -207,14 +207,14 @@ export default function CoinsDisplay({
|
||||
<CoinsDisplay value={1500} />
|
||||
|
||||
// Пример 2: Получение данных по username с автообновлением
|
||||
<CoinsDisplay
|
||||
username="player123"
|
||||
<CoinsDisplay
|
||||
username="player123"
|
||||
autoUpdate={true}
|
||||
updateInterval={30000} // обновлять каждые 30 секунд
|
||||
/>
|
||||
|
||||
// Пример 3: Кастомная стилизация без иконки
|
||||
<CoinsDisplay
|
||||
<CoinsDisplay
|
||||
value={9999}
|
||||
size="small"
|
||||
showIcon={false}
|
||||
@ -224,7 +224,7 @@ export default function CoinsDisplay({
|
||||
/>
|
||||
|
||||
// Пример 4: Большой отображение для профиля
|
||||
<CoinsDisplay
|
||||
<CoinsDisplay
|
||||
username="player123"
|
||||
size="large"
|
||||
tooltipText="Ваш текущий баланс"
|
||||
|
||||
180
src/renderer/components/Notifications/CustomNotification.tsx
Normal file
180
src/renderer/components/Notifications/CustomNotification.tsx
Normal file
@ -0,0 +1,180 @@
|
||||
import * as React from 'react';
|
||||
import Snackbar, { SnackbarCloseReason } from '@mui/material/Snackbar';
|
||||
import Alert from '@mui/material/Alert';
|
||||
|
||||
export type NotificationVertical = 'top' | 'bottom';
|
||||
export type NotificationHorizontal = 'left' | 'center' | 'right';
|
||||
|
||||
export type NotificationPosition = {
|
||||
vertical: NotificationVertical;
|
||||
horizontal: NotificationHorizontal;
|
||||
};
|
||||
|
||||
export type NotificationSeverity = 'success' | 'info' | 'warning' | 'error';
|
||||
|
||||
export interface CustomNotificationProps {
|
||||
open: boolean;
|
||||
message: React.ReactNode;
|
||||
onClose: () => void;
|
||||
|
||||
severity?: NotificationSeverity;
|
||||
position?: NotificationPosition;
|
||||
|
||||
autoHideDuration?: number;
|
||||
variant?: 'filled' | 'outlined' | 'standard';
|
||||
}
|
||||
|
||||
const getAccent = (severity: NotificationSeverity) => {
|
||||
switch (severity) {
|
||||
case 'success':
|
||||
return {
|
||||
// glow: 'rgba(43, 255, 0, 0.45)',
|
||||
// a1: 'rgba(43, 255, 0, 0.90)',
|
||||
// a2: 'rgba(0, 255, 170, 0.55)',
|
||||
// a3: 'rgba(0, 200, 120, 0.35)',
|
||||
glow: 'rgba(138, 35, 135, 0.45)',
|
||||
a1: 'rgba(242, 113, 33, 0.90)',
|
||||
a2: 'rgba(138, 35, 135, 0.90)',
|
||||
a3: 'rgba(233, 64, 205, 0.90)',
|
||||
};
|
||||
case 'warning':
|
||||
return {
|
||||
glow: 'rgba(255, 193, 7, 0.45)',
|
||||
a1: 'rgba(255, 193, 7, 0.90)',
|
||||
a2: 'rgba(255, 120, 0, 0.55)',
|
||||
a3: 'rgba(255, 80, 0, 0.35)',
|
||||
};
|
||||
case 'error':
|
||||
return {
|
||||
glow: 'rgba(255, 77, 77, 0.50)',
|
||||
a1: 'rgba(255, 77, 77, 0.90)',
|
||||
a2: 'rgba(233, 64, 87, 0.65)',
|
||||
a3: 'rgba(138, 35, 135, 0.45)',
|
||||
};
|
||||
case 'info':
|
||||
default:
|
||||
return {
|
||||
glow: 'rgba(33, 150, 243, 0.45)',
|
||||
a1: 'rgba(33, 150, 243, 0.90)',
|
||||
a2: 'rgba(0, 255, 255, 0.45)',
|
||||
a3: 'rgba(120, 60, 255, 0.35)',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export default function CustomNotification({
|
||||
open,
|
||||
message,
|
||||
onClose,
|
||||
severity = 'info',
|
||||
position = { vertical: 'bottom', horizontal: 'center' },
|
||||
autoHideDuration = 3000,
|
||||
variant = 'filled',
|
||||
}: CustomNotificationProps) {
|
||||
const accent = getAccent(severity);
|
||||
|
||||
const handleClose = (
|
||||
_event?: React.SyntheticEvent | Event,
|
||||
reason?: SnackbarCloseReason
|
||||
) => {
|
||||
if (reason === 'clickaway') return;
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Snackbar
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
autoHideDuration={autoHideDuration}
|
||||
anchorOrigin={position}
|
||||
sx={{
|
||||
'& .MuiSnackbarContent-root': {
|
||||
background: 'transparent',
|
||||
boxShadow: 'none',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Alert
|
||||
onClose={handleClose}
|
||||
severity={severity}
|
||||
variant={variant}
|
||||
icon={false}
|
||||
sx={{
|
||||
width: '100%',
|
||||
borderRadius: '1vw',
|
||||
px: '2vw',
|
||||
py: '1vw',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
|
||||
// базовый фон как в тултипе
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.88)',
|
||||
color: '#fff',
|
||||
fontFamily: 'Benzin-Bold, sans-serif',
|
||||
|
||||
// рамка + неоновая подсветка
|
||||
border: `1px solid ${accent.a2}`,
|
||||
boxShadow: `
|
||||
0 0 1.6vw ${accent.glow},
|
||||
0 0 0.6vw rgba(0, 0, 0, 0.35),
|
||||
inset 0 0 0.6vw rgba(0, 0, 0, 0.45)
|
||||
`,
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
|
||||
// внутренний градиентный бордер как у CustomTooltip
|
||||
'&::before': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
borderRadius: '1vw',
|
||||
padding: '2px',
|
||||
background: `
|
||||
linear-gradient(
|
||||
135deg,
|
||||
${accent.a1} 0%,
|
||||
${accent.a2} 50%,
|
||||
${accent.a3} 100%
|
||||
)
|
||||
`,
|
||||
WebkitMask:
|
||||
'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
|
||||
WebkitMaskComposite: 'xor',
|
||||
maskComposite: 'exclude',
|
||||
pointerEvents: 'none',
|
||||
zIndex: 0,
|
||||
},
|
||||
|
||||
// контент поверх ::before
|
||||
'& .MuiAlert-message': {
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
padding: 0,
|
||||
fontSize: '1.5vw',
|
||||
lineHeight: 1.25,
|
||||
},
|
||||
|
||||
// кнопка закрытия
|
||||
'& .MuiAlert-action': {
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
alignItems: 'center',
|
||||
padding: 0,
|
||||
marginLeft: '1vw',
|
||||
},
|
||||
'& .MuiIconButton-root': {
|
||||
color: 'rgba(255,255,255,0.85)',
|
||||
transition: 'all 0.2s ease',
|
||||
'&:hover': {
|
||||
color: accent.a1,
|
||||
transform: 'scale(1.08)',
|
||||
backgroundColor: 'rgba(255,255,255,0.06)',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{message}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
);
|
||||
}
|
||||
@ -39,6 +39,13 @@ export default function PageHeader() {
|
||||
};
|
||||
}
|
||||
|
||||
if (path.startsWith('/daily')) {
|
||||
return {
|
||||
title: 'Ежедневные награды',
|
||||
subtitle: 'Ежедневный вход на сервер приносит бонусы и полезные награды!',
|
||||
};
|
||||
}
|
||||
|
||||
if (path.startsWith('/profile')) {
|
||||
return {
|
||||
title: 'Профиль пользователя',
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
} from '@mui/material';
|
||||
import { claimDaily, fetchDailyStatus, DailyStatusResponse } from '../../api';
|
||||
import CoinsDisplay from '../CoinsDisplay';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
function formatHHMMSS(totalSeconds: number) {
|
||||
const s = Math.max(0, Math.floor(totalSeconds));
|
||||
@ -37,6 +38,7 @@ type DailyStatusCompat = DailyStatusResponse & {
|
||||
};
|
||||
|
||||
export default function DailyRewards({ onClaimed, onOpenGame }: Props) {
|
||||
const navigate = useNavigate();
|
||||
const [status, setStatus] = useState<DailyStatusCompat | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [tick, setTick] = useState(0);
|
||||
@ -129,11 +131,14 @@ export default function DailyRewards({ onClaimed, onOpenGame }: Props) {
|
||||
return `До следующей награды: ${formatHHMMSS(clientSecondsLeft)}`;
|
||||
}, [status, wasOnlineToday, canClaim, clientSecondsLeft]);
|
||||
|
||||
const navigateDaily = () => {
|
||||
navigate('/daily');
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
sx={{
|
||||
width: '90%',
|
||||
maxWidth: 520,
|
||||
width: '100%',
|
||||
background: 'rgba(20,20,20,0.9)',
|
||||
borderRadius: '2vw',
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
@ -227,6 +232,22 @@ export default function DailyRewards({ onClaimed, onOpenGame }: Props) {
|
||||
>
|
||||
{loading ? 'Забираем...' : 'Забрать награду'}
|
||||
</Button>
|
||||
<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>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
|
||||
@ -5,7 +5,7 @@ import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Tooltip } from '@mui/material';
|
||||
import { fetchCoins } from '../api';
|
||||
import CustomTooltip from './CustomTooltip';
|
||||
import CustomTooltip from './Notifications/CustomTooltip';
|
||||
import CoinsDisplay from './CoinsDisplay';
|
||||
declare global {
|
||||
interface Window {
|
||||
|
||||
Reference in New Issue
Block a user