itcloud/frontend/src/pages/ShareViewPage.tsx

180 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import {
Box,
Container,
Paper,
Typography,
CircularProgress,
Alert,
TextField,
Button,
} from '@mui/material';
import { CloudUpload as CloudIcon } from '@mui/icons-material';
import ViewerModal from '../components/ViewerModal';
import type { Share, Asset } from '../types';
import api from '../services/api';
export default function ShareViewPage() {
const { token } = useParams<{ token: string }>();
const [share, setShare] = useState<Share | null>(null);
const [asset, setAsset] = useState<Asset | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [password, setPassword] = useState('');
const [needsPassword, setNeedsPassword] = useState(false);
const [viewerOpen, setViewerOpen] = useState(false);
useEffect(() => {
if (token) {
loadShare();
}
}, [token]);
const loadShare = async (pwd?: string) => {
if (!token) return;
try {
setLoading(true);
setError('');
const response = await api.getShare(token, pwd);
setShare(response.share);
if (response.asset) {
setAsset(response.asset);
}
} catch (err: any) {
if (err.response?.status === 401) {
setNeedsPassword(true);
setError('Требуется пароль');
} else {
setError(err.response?.data?.detail || 'Ссылка недействительна или истекла');
}
} finally {
setLoading(false);
}
};
const handlePasswordSubmit = (e: React.FormEvent) => {
e.preventDefault();
loadShare(password);
};
const handleView = () => {
setViewerOpen(true);
};
return (
<Box
sx={{
minHeight: '100vh',
display: 'flex',
alignItems: 'center',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
p: { xs: 1, sm: 2 },
}}
>
<Container maxWidth="md">
<Paper
elevation={24}
sx={{
p: { xs: 2, sm: 3, md: 4 },
borderRadius: 3,
}}
>
<Box sx={{ textAlign: 'center', mb: 3 }}>
<CloudIcon sx={{ fontSize: { xs: 48, sm: 64 }, color: 'primary.main', mb: 2 }} />
<Typography variant="h4" fontWeight="bold" gutterBottom sx={{ fontSize: { xs: '1.5rem', sm: '2.125rem' } }}>
Общий доступ к файлу
</Typography>
</Box>
{loading && (
<Box sx={{ display: 'flex', justifyContent: 'center', p: 4 }}>
<CircularProgress />
</Box>
)}
{error && !needsPassword && (
<Alert severity="error" sx={{ mb: 2 }}>
{error}
</Alert>
)}
{needsPassword && !share && (
<form onSubmit={handlePasswordSubmit}>
<Typography variant="body1" gutterBottom>
Этот файл защищен паролем
</Typography>
<TextField
fullWidth
type="password"
label="Пароль"
value={password}
onChange={(e) => setPassword(e.target.value)}
margin="normal"
required
autoFocus
/>
{error && (
<Alert severity="error" sx={{ mt: 2 }}>
{error}
</Alert>
)}
<Button
type="submit"
fullWidth
variant="contained"
size="large"
sx={{ mt: 3 }}
>
Открыть
</Button>
</form>
)}
{!loading && share && asset && (
<Box>
<Typography
variant="h6"
gutterBottom
sx={{
wordBreak: 'break-word',
overflowWrap: 'anywhere',
fontSize: { xs: '1rem', sm: '1.25rem' },
}}
>
{asset.original_filename}
</Typography>
<Typography variant="body2" color="text.secondary" gutterBottom>
Размер: {(asset.size_bytes / 1024 / 1024).toFixed(2)} MB
</Typography>
<Typography variant="body2" color="text.secondary" gutterBottom>
Тип: {asset.type === 'photo' ? 'Фото' : 'Видео'}
</Typography>
<Button
fullWidth
variant="contained"
size="large"
sx={{ mt: 3 }}
onClick={handleView}
>
Просмотреть
</Button>
</Box>
)}
</Paper>
{asset && (
<ViewerModal
asset={viewerOpen ? asset : null}
assets={[asset]}
onClose={() => setViewerOpen(false)}
/>
)}
</Container>
</Box>
);
}