diff --git a/backend-rs/src/dto.rs b/backend-rs/src/dto.rs index 1df29e9..1556d79 100644 --- a/backend-rs/src/dto.rs +++ b/backend-rs/src/dto.rs @@ -109,7 +109,6 @@ pub struct UserOut { pub email: String, pub theme: String, pub language: String, - pub drawer_anchor: String, pub avatar: Option, pub following_count: i64, pub followers_count: i64, diff --git a/backend-rs/src/entities/user.rs b/backend-rs/src/entities/user.rs index 8d625af..d0fb213 100644 --- a/backend-rs/src/entities/user.rs +++ b/backend-rs/src/entities/user.rs @@ -25,7 +25,6 @@ pub struct Model { pub avatar: Option, pub language: String, pub selected_banner_id: Option, - pub drawer_anchor: String, /// Dedicated account-deletion confirmation code + its expiry. Kept separate /// from `email_confirmation_code` so other code-driven flows (email confirm, /// password reset) can never clobber an in-flight deletion code, and diff --git a/backend-rs/src/routes/users.rs b/backend-rs/src/routes/users.rs index 47b928a..cf0d8c8 100644 --- a/backend-rs/src/routes/users.rs +++ b/backend-rs/src/routes/users.rs @@ -63,7 +63,6 @@ const THEMES: &[&str] = &[ "t0p_clancy", ]; const LANGUAGES: &[&str] = &["en", "de"]; -const DRAWER_ANCHORS: &[&str] = &["left", "right"]; pub fn router() -> Router { Router::new() @@ -434,12 +433,6 @@ async fn update_preferences( updated = true; } } - if let Some(da) = data.get("drawer_anchor").and_then(|v| v.as_str()) { - if DRAWER_ANCHORS.contains(&da) { - active.drawer_anchor = Set(da.to_string()); - updated = true; - } - } // selected_banner: present + non-null. "" clears; otherwise must be enabled. match data.get("selected_banner") { @@ -489,7 +482,6 @@ async fn update_preferences( "status": "preferences updated", "theme": saved.theme, "language": saved.language, - "drawer_anchor": saved.drawer_anchor, "selected_banner": saved.selected_banner_id, }))) } @@ -763,7 +755,6 @@ async fn signup( avatar: Set(None), language: Set("en".into()), selected_banner_id: Set(None), - drawer_anchor: Set("right".into()), ..Default::default() } .insert(&state.db) diff --git a/backend-rs/src/service.rs b/backend-rs/src/service.rs index d76ffaf..1bf9eb2 100644 --- a/backend-rs/src/service.rs +++ b/backend-rs/src/service.rs @@ -758,7 +758,6 @@ pub async fn build_users( email: u.email, theme: u.theme, language: u.language, - drawer_anchor: u.drawer_anchor, }) .collect()) } diff --git a/frontend/src/contexts/auth-context.ts b/frontend/src/contexts/auth-context.ts index 5044e20..ec0ded1 100644 --- a/frontend/src/contexts/auth-context.ts +++ b/frontend/src/contexts/auth-context.ts @@ -7,7 +7,6 @@ export interface AuthUser { is_superuser: boolean; theme?: string; language?: string; - drawer_anchor?: 'left' | 'right'; } export interface FollowingEntry { diff --git a/frontend/src/hooks/useDrawerAnchor.ts b/frontend/src/hooks/useDrawerAnchor.ts deleted file mode 100644 index f8fd95e..0000000 --- a/frontend/src/hooks/useDrawerAnchor.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { useCallback, useEffect, useState } from 'react'; -import api from '../lib/api'; -import { useAuth } from './useAuth'; - -export type DrawerAnchor = 'left' | 'right'; - -const STORAGE_KEY = 'drawer-anchor'; - -const readLocal = (): DrawerAnchor => { - const raw = localStorage.getItem(STORAGE_KEY); - return raw === 'left' ? 'left' : 'right'; -}; - -export interface UseDrawerAnchorResult { - anchor: DrawerAnchor; - setAnchor: (anchor: DrawerAnchor) => Promise; -} - -export const useDrawerAnchor = (): UseDrawerAnchorResult => { - const { user, refetchUser } = useAuth(); - const [anchor, setAnchorState] = useState( - (user?.drawer_anchor as DrawerAnchor) ?? readLocal(), - ); - - // Adopt the server-side preference when it loads/changes. Derived during - // render (tracking the previously-applied value) so it doesn't cost an extra - // effect pass; the localStorage mirror is written in the effect below. - const [appliedAnchor, setAppliedAnchor] = useState(user?.drawer_anchor); - if (user?.drawer_anchor && user.drawer_anchor !== appliedAnchor) { - setAppliedAnchor(user.drawer_anchor); - setAnchorState(user.drawer_anchor); - } - - useEffect(() => { - if (user?.drawer_anchor) localStorage.setItem(STORAGE_KEY, user.drawer_anchor); - }, [user?.drawer_anchor]); - - const setAnchor = useCallback( - async (next: DrawerAnchor) => { - setAnchorState(next); - localStorage.setItem(STORAGE_KEY, next); - if (user) { - try { - await api.patch('users/update_preferences/', { drawer_anchor: next }); - await refetchUser(); - } catch { - /* fall back to local-only persistence */ - } - } - }, - [user, refetchUser], - ); - - return { anchor, setAnchor }; -}; diff --git a/frontend/src/pages/MainPage.tsx b/frontend/src/pages/MainPage.tsx index 98a84f9..f9e5b2b 100644 --- a/frontend/src/pages/MainPage.tsx +++ b/frontend/src/pages/MainPage.tsx @@ -9,7 +9,6 @@ import { IconButton, Skeleton, useMediaQuery, - useTheme, } from '@mui/material'; import { Link, useLocation } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; @@ -94,8 +93,6 @@ const MainPage: React.FC = () => { const [autoPlay, setAutoPlay] = useState(true); const [hovered, setHovered] = useState(false); const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)'); - const muiTheme = useTheme(); - const isDesktop = useMediaQuery(muiTheme.breakpoints.up('md')); const touchStartX = useRef(null); const query = new URLSearchParams(location.search).get('q') || ''; @@ -533,8 +530,8 @@ const MainPage: React.FC = () => { )} - {/* Desktop-only category browser */} - {isDesktop && } + {/* Category browser */} + {/* Algorithmic discovery: personalized (members) or popular picks (visitors) */} diff --git a/frontend/src/pages/Settings.tsx b/frontend/src/pages/Settings.tsx index 7e98334..a765577 100644 --- a/frontend/src/pages/Settings.tsx +++ b/frontend/src/pages/Settings.tsx @@ -24,8 +24,6 @@ import { ListSubheader, Divider, Grid, - FormControlLabel, - Switch, } from '@mui/material'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import { useNavigate, useSearchParams } from 'react-router-dom'; @@ -36,7 +34,6 @@ import { useTitle } from '../hooks/useTitle'; import type { CatppuccinTheme } from '../theme'; import { PageShell, GlassCard, FormCard } from '../components/ui'; import { useConfirm } from '../hooks/useConfirm'; -import { useDrawerAnchor } from '../hooks/useDrawerAnchor'; interface SettingsProps { themeName: CatppuccinTheme; @@ -54,7 +51,6 @@ const Settings: React.FC = ({ themeName, onThemeChange }) => { const { refetchUser } = useAuth(); useTitle(t('nav.settings')); const { confirm } = useConfirm(); - const { anchor: drawerAnchor, setAnchor: setDrawerAnchor } = useDrawerAnchor(); const { data: me } = useMe(); const { data: banners = [] } = useBannersList(); const updateAvatarMutation = useUpdateAvatar(); @@ -324,31 +320,6 @@ const Settings: React.FC = ({ themeName, onThemeChange }) => { Deutsch - - - - setDrawerAnchor(e.target.checked ? 'left' : 'right')} - /> - } - label={ - - - {t('settings.leftHandMode', { defaultValue: 'Left-hand mode' })} - - - {t('settings.leftHandModeHint', { - defaultValue: - 'Mobile drawer slides in from the left (better for left-handed thumb reach).', - })} - - - } - sx={{ alignItems: 'flex-start', mt: 1 }} - /> {/* Avatar: NOT a FormCard — file input lives outside any form */}