Move Download Button Below Title #206

Merged
Quaternions merged 3 commits from map-access into staging 2025-06-24 06:05:50 +00:00
2 changed files with 63 additions and 73 deletions

View File

@@ -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;

View File

@@ -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>