219 lines
8.0 KiB
JavaScript
219 lines
8.0 KiB
JavaScript
import React, { useState, useEffect, useLayoutEffect, useContext } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { Grid, Card, CardContent, Typography, Box, CardMedia, IconButton, Snackbar, Alert } from '@mui/material';
|
|
import { UserContext } from '../contexts/UserContext';
|
|
import PersonAddIcon from '@mui/icons-material/PersonAdd';
|
|
import RemoveCircleOutlineIcon from '@mui/icons-material/RemoveCircleOutline';
|
|
import axios from 'axios';
|
|
|
|
import ConfirmDialog from './ConfirmDialog';
|
|
|
|
const Dashboard = () => {
|
|
const { user, setUser } = useContext(UserContext);
|
|
const [guilds, setGuilds] = useState([]);
|
|
const [botStatus, setBotStatus] = useState({});
|
|
const [snackbarOpen, setSnackbarOpen] = useState(false);
|
|
const [snackbarMessage, setSnackbarMessage] = useState('');
|
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
const [selectedGuild, setSelectedGuild] = useState(null);
|
|
const navigate = useNavigate();
|
|
|
|
useEffect(() => {
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const userParam = urlParams.get('user');
|
|
const guildsParam = urlParams.get('guilds');
|
|
|
|
if (userParam && guildsParam) {
|
|
const parsedUser = JSON.parse(decodeURIComponent(userParam));
|
|
const parsedGuilds = JSON.parse(decodeURIComponent(guildsParam));
|
|
localStorage.setItem('user', JSON.stringify(parsedUser));
|
|
localStorage.setItem('guilds', JSON.stringify(parsedGuilds));
|
|
setUser(parsedUser);
|
|
setGuilds(parsedGuilds);
|
|
// Clean the URL
|
|
window.history.replaceState({}, document.title, "/dashboard");
|
|
} else {
|
|
const storedUser = localStorage.getItem('user');
|
|
const storedGuilds = localStorage.getItem('guilds');
|
|
if (storedUser && storedGuilds) {
|
|
setUser(JSON.parse(storedUser));
|
|
setGuilds(JSON.parse(storedGuilds));
|
|
}
|
|
}
|
|
}, [setUser]);
|
|
|
|
useEffect(() => {
|
|
const fetchBotStatus = async () => {
|
|
const statusPromises = guilds.map(async (guild) => {
|
|
try {
|
|
const response = await axios.get(`http://localhost:3002/api/servers/${guild.id}/bot-status`);
|
|
return { guildId: guild.id, isBotInServer: response.data.isBotInServer };
|
|
} catch (error) {
|
|
console.error(`Error fetching bot status for guild ${guild.id}:`, error);
|
|
return { guildId: guild.id, isBotInServer: false };
|
|
}
|
|
});
|
|
const results = await Promise.all(statusPromises);
|
|
const newBotStatus = results.reduce((acc, curr) => {
|
|
acc[curr.guildId] = curr.isBotInServer;
|
|
return acc;
|
|
}, {});
|
|
setBotStatus(newBotStatus);
|
|
};
|
|
|
|
if (guilds.length > 0) {
|
|
fetchBotStatus();
|
|
}
|
|
}, [guilds]);
|
|
|
|
useLayoutEffect(() => {
|
|
const scrollPosition = sessionStorage.getItem('scrollPosition');
|
|
if (scrollPosition) {
|
|
window.scrollTo(0, parseInt(scrollPosition));
|
|
sessionStorage.removeItem('scrollPosition');
|
|
}
|
|
}, []);
|
|
|
|
const handleCardClick = (guild) => {
|
|
sessionStorage.setItem('scrollPosition', window.scrollY);
|
|
navigate(`/server/${guild.id}`, { state: { guild } });
|
|
};
|
|
|
|
const handleInviteBot = async (e, guild) => {
|
|
e.stopPropagation();
|
|
const clientId = '1423377662055026840'; // Hardcoded client ID from user request
|
|
const permissions = 8; // Administrator
|
|
const inviteUrl = `https://discord.com/api/oauth2/authorize?client_id=${clientId}&permissions=${permissions}&scope=bot%20applications.commands&guild_id=${guild.id}&disable_guild_select=true`;
|
|
window.open(inviteUrl, '_blank', 'noopener,noreferrer');
|
|
};
|
|
|
|
const handleLeaveBot = (e, guild) => {
|
|
e.stopPropagation();
|
|
setSelectedGuild(guild);
|
|
setDialogOpen(true);
|
|
};
|
|
|
|
const handleConfirmLeave = async () => {
|
|
if (!selectedGuild) return;
|
|
try {
|
|
await axios.post(`http://localhost:3002/api/servers/${selectedGuild.id}/leave`);
|
|
setBotStatus(prevStatus => ({ ...prevStatus, [selectedGuild.id]: false }));
|
|
setSnackbarMessage('Bot has left the server.');
|
|
setSnackbarOpen(true);
|
|
} catch (error) {
|
|
console.error('Error leaving server:', error);
|
|
setSnackbarMessage('Failed to make the bot leave the server.');
|
|
setSnackbarOpen(true);
|
|
}
|
|
setDialogOpen(false);
|
|
setSelectedGuild(null);
|
|
};
|
|
|
|
const handleSnackbarClose = (event, reason) => {
|
|
if (reason === 'clickaway') {
|
|
return;
|
|
}
|
|
setSnackbarOpen(false);
|
|
};
|
|
|
|
return (
|
|
<div style={{ padding: '20px' }}>
|
|
{/* UserSettings moved to NavBar */}
|
|
<Typography variant="h4" gutterBottom>
|
|
Dashboard
|
|
</Typography>
|
|
{user && (
|
|
<Typography variant="h5" gutterBottom>
|
|
Welcome, {user.username}
|
|
</Typography>
|
|
)}
|
|
<Typography variant="h6" gutterBottom>
|
|
Your Admin Servers:
|
|
</Typography>
|
|
<Grid container spacing={3}>
|
|
{guilds.map(guild => (
|
|
<Grid item xs={12} sm={6} md={4} lg={3} key={guild.id}>
|
|
<Card
|
|
onClick={() => handleCardClick(guild)}
|
|
sx={{
|
|
cursor: 'pointer',
|
|
borderRadius: '20px',
|
|
boxShadow: '0 8px 16px 0 rgba(0,0,0,0.2)',
|
|
transition: 'transform 0.3s',
|
|
height: '250px',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
justifyContent: 'space-between',
|
|
'&:hover': {
|
|
transform: 'scale(1.05)'
|
|
}
|
|
}}
|
|
>
|
|
<CardMedia
|
|
component="img"
|
|
sx={{ height: '60%', objectFit: 'cover' }}
|
|
image={guild.icon ? `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.png` : 'https://cdn.discordapp.com/embed/avatars/0.png'}
|
|
alt={guild.name}
|
|
/>
|
|
<CardContent>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1, flexDirection: { xs: 'column', sm: 'row' } }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, width: '100%', justifyContent: { xs: 'center', sm: 'flex-start' } }}>
|
|
<Box
|
|
title={guild.name}
|
|
sx={{
|
|
px: 2,
|
|
py: 0.5,
|
|
borderRadius: '999px',
|
|
fontWeight: 'bold',
|
|
bgcolor: 'rgba(0,0,0,0.06)',
|
|
maxWidth: '100%',
|
|
overflow: 'hidden',
|
|
textOverflow: 'ellipsis',
|
|
whiteSpace: 'nowrap',
|
|
textAlign: { xs: 'center', sm: 'left' }
|
|
}}
|
|
>
|
|
{guild.name}
|
|
</Box>
|
|
</Box>
|
|
{botStatus[guild.id] ? (
|
|
<IconButton
|
|
aria-label={`Make bot leave ${guild.name}`}
|
|
size="small"
|
|
onClick={(e) => handleLeaveBot(e, guild)}
|
|
>
|
|
<RemoveCircleOutlineIcon />
|
|
</IconButton>
|
|
) : (
|
|
<IconButton
|
|
aria-label={`Invite bot to ${guild.name}`}
|
|
size="small"
|
|
onClick={(e) => handleInviteBot(e, guild)}
|
|
>
|
|
<PersonAddIcon />
|
|
</IconButton>
|
|
)}
|
|
</Box>
|
|
</CardContent>
|
|
</Card>
|
|
</Grid>
|
|
))}
|
|
</Grid>
|
|
<Snackbar open={snackbarOpen} autoHideDuration={6000} onClose={handleSnackbarClose}>
|
|
<Alert onClose={handleSnackbarClose} severity="info" sx={{ width: '100%' }}>
|
|
{snackbarMessage}
|
|
</Alert>
|
|
</Snackbar>
|
|
<ConfirmDialog
|
|
open={dialogOpen}
|
|
onClose={() => setDialogOpen(false)}
|
|
onConfirm={handleConfirmLeave}
|
|
title="Confirm Leave"
|
|
message={`Are you sure you want the bot to leave ${selectedGuild?.name}?`}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Dashboard;
|