From 4753bd57918d2699b4e4a190cf91e128c13c2c03 Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Tue, 17 Feb 2026 20:58:18 +0100 Subject: [PATCH] fixed spacing on mobile and moved notification --- frontend/src/App.tsx | 201 ++++++++++++++++----------------- frontend/src/pages/About.tsx | 23 ++-- frontend/src/pages/Privacy.tsx | 59 +++++----- 3 files changed, 142 insertions(+), 141 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index fc79515..a1511a7 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -26,7 +26,8 @@ import { ListItemAvatar, ListSubheader, Badge, - alpha + alpha, + useMediaQuery } from '@mui/material'; import MenuIcon from '@mui/icons-material/Menu'; import SearchIcon from '@mui/icons-material/Search'; @@ -82,6 +83,7 @@ const GlobalSearch = () => { const [loading, setLoading] = useState(false); const navigate = useNavigate(); const location = useLocation(); + const isMobile = useMediaQuery('(max-width:900px)'); // Debounced search useEffect(() => { @@ -122,14 +124,14 @@ const GlobalSearch = () => { }; return ( - + x} // Disable local filtering as we do it on server + filterOptions={(x) => x} getOptionLabel={(option) => { if (typeof option === 'string') return option; return option.name || ''; @@ -209,6 +211,7 @@ const App: React.FC = () => { const [loadingUser, setLoadingUser] = useState(true); const [drawerOpen, setDrawerOpen] = useState(false); const [catOpen, setCatOpen] = useState(false); + const isMobile = useMediaQuery('(max-width:900px)'); const fetchNotifications = async () => { try { @@ -234,7 +237,6 @@ const App: React.FC = () => { if (userRes.data.theme) setThemeName(userRes.data.theme); if (userRes.data.language) i18n.changeLanguage(userRes.data.language); - // Also fetch following list for the mobile sidebar try { const followRes = await api.get('users/following_list/'); const followData = Array.isArray(followRes.data) ? followRes.data : (followRes.data.results || []); @@ -257,11 +259,9 @@ const App: React.FC = () => { }; fetchData(); - // Poll for notifications every 60 seconds const interval = setInterval(() => { if (localStorage.getItem('token')) { fetchNotifications(); - // Also refresh user data for the count api.get('users/me/').then(res => setUser(res.data)).catch(() => {}); } }, 60000); @@ -364,14 +364,6 @@ const App: React.FC = () => { {user ? ( <> - - setNotifAnchorEl(e.currentTarget)}> - 0 ? `${user.unread_notifications_count} unread` : null} - /> - - setDrawerOpen(false)}> { ); + const notificationsMenu = ( + <> + setNotifAnchorEl(e.currentTarget)} sx={{ ml: 1 }}> + + + + + setNotifAnchorEl(null)} + elevation={3} + sx={{ mt: 1 }} + PaperProps={{ sx: { width: 320, maxHeight: 400 } }} + > + + Notifications + {(user?.unread_notifications_count || 0) > 0 && ( + + )} + + {notifications.length === 0 ? ( + + No notifications yet + + ) : ( + notifications.map(n => ( + handleNotificationClick(n)} + sx={{ + py: 1.5, + px: 2, + borderBottom: '1px solid', + borderColor: 'divider', + bgcolor: n.is_read ? 'transparent' : alpha(theme.palette.primary.main, 0.05), + whiteSpace: 'normal', + display: 'flex', + gap: 2, + alignItems: 'flex-start' + }} + > + + {n.actor_username.charAt(0).toUpperCase()} + + + + {n.actor_username} {n.notification_type === 'reply' ? 'replied' : 'mentioned you'} on {n.flavor_name} + + + {formatDate(n.created_at)} + + + + )) + )} + + + ); + return ( @@ -453,14 +505,14 @@ const App: React.FC = () => { }} > - - - + + Holy Flavors Archive + HFA - + {user && ( - )} - - {notifications.length === 0 ? ( - - No notifications yet - - ) : ( - notifications.map(n => ( - handleNotificationClick(n)} - sx={{ - py: 1.5, - px: 2, - borderBottom: '1px solid', - borderColor: 'divider', - bgcolor: n.is_read ? 'transparent' : alpha(theme.palette.primary.main, 0.05), - whiteSpace: 'normal', - display: 'flex', - gap: 2, - alignItems: 'flex-start' - }} - > - - {n.actor_username.charAt(0).toUpperCase()} - - - - {n.actor_username} {n.notification_type === 'reply' ? 'replied to your review' : 'mentioned you'} on {n.flavor_name} - - - {formatDate(n.created_at)} - - - - )) - )} - - - setAnchorEl(e.currentTarget)} sx={{ ml: 1 }}> - - {user.username ? user.username.charAt(0).toUpperCase() : '?'} - - - setAnchorEl(null)} - elevation={3} - sx={{ mt: 1 }} - > - setAnchorEl(null)}>{t('nav.profile')} - setAnchorEl(null)}>{t('nav.dashboard')} - setAnchorEl(null)}>{t('nav.settings')} - - {t('nav.logout')} - - - ) : ( - - - - ) + > + setAnchorEl(null)}>{t('nav.profile')} + setAnchorEl(null)}>{t('nav.dashboard')} + setAnchorEl(null)}>{t('nav.settings')} + + {t('nav.logout')} + + + ) : ( + + + + )} + )} - + {isMobile && ( setDrawerOpen(true)} sx={{ ml: 1 }} > - + )} diff --git a/frontend/src/pages/About.tsx b/frontend/src/pages/About.tsx index f5babb7..f7d8524 100644 --- a/frontend/src/pages/About.tsx +++ b/frontend/src/pages/About.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Typography, Box, Container, Paper, Divider, Button } from '@mui/material'; +import { Typography, Box, Container, Paper, Divider, Button, useMediaQuery } from '@mui/material'; import { Link } from 'react-router-dom'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import { useTranslation } from 'react-i18next'; @@ -8,9 +8,10 @@ import { useTitle } from '../hooks/useTitle'; const About: React.FC = () => { const { t } = useTranslation(); useTitle(t('nav.about')); + const isMobile = useMediaQuery('(max-width:600px)'); return ( - + - - {t('about.title')} - + + {t('about.title')} + {t('about.mission')} - {t('about.canDoTitle')} - + {t('about.canDoTitle')} +
  • {t('about.featureExplore')}
  • {t('about.featureRate')}
  • {t('about.featureCommunity')}
  • @@ -41,9 +42,9 @@ const About: React.FC = () => { {t('about.disclaimer')}
    - + - {t('about.impressumTitle')} + {t('about.impressumTitle')} {t('about.legalInfo')}
    {import.meta.env.VITE_IMPRESSUM_NAME || '[Your Name or Project Name]'}
    @@ -74,12 +75,12 @@ const About: React.FC = () => { {t('about.disclaimerTitle')} - + Haftung für Inhalte: Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen. - + Urheberrecht: Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors. diff --git a/frontend/src/pages/Privacy.tsx b/frontend/src/pages/Privacy.tsx index 999d85a..f3808ba 100644 --- a/frontend/src/pages/Privacy.tsx +++ b/frontend/src/pages/Privacy.tsx @@ -1,15 +1,16 @@ -import React from 'react'; -import { Typography, Box, Container, Paper, Button, ToggleButton, ToggleButtonGroup } from '@mui/material'; +import React, { useState } from 'react'; +import { Typography, Box, Container, Paper, Button, ToggleButton, ToggleButtonGroup, useMediaQuery } from '@mui/material'; import { Link } from 'react-router-dom'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import { useTitle } from '../hooks/useTitle'; const Privacy: React.FC = () => { useTitle('Privacy Policy / Datenschutzerklärung'); - const [lang, setLang] = React.useState<'de' | 'en'>('de'); + const [lang, setLang] = useState<'de' | 'en'>('de'); + const isMobile = useMediaQuery('(max-width:600px)'); return ( - +