Move Download Button Below Title #206
@@ -1,69 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button } from '@mui/material';
|
||||
import Download from '@mui/icons-material/Download';
|
||||
|
||||
interface DownloadButtonProps {
|
||||
assetId: number;
|
||||
assetName: string;
|
||||
}
|
||||
|
||||
const DownloadButton: React.FC<DownloadButtonProps> = ({ assetId, assetName }) => {
|
||||
const [downloading, setDownloading] = useState(false);
|
||||
|
||||
const handleDownload = async () => {
|
||||
setDownloading(true);
|
||||
try {
|
||||
// Fetch the download URL
|
||||
const res = await fetch(`/api/maps/${assetId}/location`);
|
||||
if (!res.ok) throw new Error('Failed to fetch download location');
|
||||
|
||||
const location = await res.text();
|
||||
|
||||
// Method 1: Try direct download with proper cleanup
|
||||
try {
|
||||
const link = document.createElement('a');
|
||||
link.href = location.trim(); // Remove any whitespace
|
||||
link.download = `${assetName}.rbxm`;
|
||||
link.target = '_blank'; // Open in new tab as fallback
|
||||
link.rel = 'noopener noreferrer'; // Security best practice
|
||||
|
||||
// Ensure the link is properly attached before clicking
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
// Clean up after a short delay to ensure download starts
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(link);
|
||||
}, 100);
|
||||
|
||||
} catch (domError) {
|
||||
console.warn('Direct download failed, trying fallback:', domError);
|
||||
// Method 2: Fallback - open in new window
|
||||
window.open(location.trim(), '_blank');
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error('Download error:', err);
|
||||
// Optional: Show user-friendly error message
|
||||
alert('Download failed. Please try again.');
|
||||
} finally {
|
||||
setDownloading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
fullWidth
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<Download />}
|
||||
size="large"
|
||||
onClick={handleDownload}
|
||||
disabled={downloading}
|
||||
>
|
||||
{downloading ? 'Downloading...' : 'Download'}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export default DownloadButton;
|
||||
@@ -30,7 +30,8 @@ import PersonIcon from "@mui/icons-material/Person";
|
||||
import FlagIcon from "@mui/icons-material/Flag";
|
||||
import BugReportIcon from "@mui/icons-material/BugReport";
|
||||
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
||||
import DownloadButton from "@/app/_components/downloadButton";
|
||||
import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile";
|
||||
import DownloadIcon from '@mui/icons-material/Download';
|
||||
import { hasRole, RolesConstants } from "@/app/ts/Roles";
|
||||
|
||||
export default function MapDetails() {
|
||||
@@ -41,6 +42,7 @@ export default function MapDetails() {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [copySuccess, setCopySuccess] = useState(false);
|
||||
const [roles, setRoles] = useState(RolesConstants.Empty);
|
||||
const [downloading, setDownloading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
async function getMap() {
|
||||
@@ -124,6 +126,48 @@ export default function MapDetails() {
|
||||
setCopySuccess(true);
|
||||
};
|
||||
|
||||
|
||||
const handleDownload = async () => {
|
||||
setDownloading(true);
|
||||
try {
|
||||
// Fetch the download URL
|
||||
const res = await fetch(`/api/maps/${mapId}/location`);
|
||||
if (!res.ok) throw new Error('Failed to fetch download location');
|
||||
|
||||
const location = await res.text();
|
||||
|
||||
// Method 1: Try direct download with proper cleanup
|
||||
try {
|
||||
const link = document.createElement('a');
|
||||
link.href = location.trim(); // Remove any whitespace
|
||||
link.download = `${map?.DisplayName}.rbxm`;
|
||||
link.target = '_blank'; // Open in new tab as fallback
|
||||
link.rel = 'noopener noreferrer'; // Security best practice
|
||||
|
||||
// Ensure the link is properly attached before clicking
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
// Clean up after a short delay to ensure download starts
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(link);
|
||||
}, 100);
|
||||
|
||||
} catch (domError) {
|
||||
console.warn('Direct download failed, trying fallback:', domError);
|
||||
// Method 2: Fallback - open in new window
|
||||
window.open(location.trim(), '_blank');
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error('Download error:', err);
|
||||
// Optional: Show user-friendly error message
|
||||
alert('Download failed. Please try again.');
|
||||
} finally {
|
||||
setDownloading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCloseSnackbar = () => {
|
||||
setCopySuccess(false);
|
||||
};
|
||||
@@ -255,6 +299,24 @@ export default function MapDetails() {
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
{!loading && hasRole(roles,RolesConstants.MapDownload) && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<InsertDriveFileIcon sx={{ mr: 1, color: 'primary.main' }} />
|
||||
<Typography variant="body1">
|
||||
Download
|
||||
</Typography>
|
||||
<Tooltip title="File extension must be changed to .rbxm manually">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={handleDownload}
|
||||
sx={{ ml: 1 }}
|
||||
disabled={downloading}
|
||||
>
|
||||
<DownloadIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -337,9 +399,6 @@ export default function MapDetails() {
|
||||
>
|
||||
Submit a Mapfix
|
||||
</Button>
|
||||
{hasRole(roles,RolesConstants.MapDownload) && (
|
||||
<DownloadButton assetId={map.ID} assetName={map.DisplayName} />
|
||||
)}
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
Reference in New Issue
Block a user