mirror of
https://github.com/narrrl/holy-flavor-rater.git
synced 2026-11-03 16:41:27 +01:00
removed old drawer stuff
This commit is contained in:
1 parent
e714e1ea51
commit
5ff5da2aac
8 files changed
+2
-102
No files matched your search
@@ -109,7 +109,6 @@ pub struct UserOut {
|
|||||||
pub email: String,
|
pub email: String,
|
||||||
pub theme: String,
|
pub theme: String,
|
||||||
pub language: String,
|
pub language: String,
|
||||||
pub drawer_anchor: String,
|
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub following_count: i64,
|
pub following_count: i64,
|
||||||
pub followers_count: i64,
|
pub followers_count: i64,
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ pub struct Model {
|
|||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub language: String,
|
pub language: String,
|
||||||
pub selected_banner_id: Option<i32>,
|
pub selected_banner_id: Option<i32>,
|
||||||
pub drawer_anchor: String,
|
|
||||||
/// Dedicated account-deletion confirmation code + its expiry. Kept separate
|
/// Dedicated account-deletion confirmation code + its expiry. Kept separate
|
||||||
/// from `email_confirmation_code` so other code-driven flows (email confirm,
|
/// from `email_confirmation_code` so other code-driven flows (email confirm,
|
||||||
/// password reset) can never clobber an in-flight deletion code, and
|
/// password reset) can never clobber an in-flight deletion code, and
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ const THEMES: &[&str] = &[
|
|||||||
"t0p_clancy",
|
"t0p_clancy",
|
||||||
];
|
];
|
||||||
const LANGUAGES: &[&str] = &["en", "de"];
|
const LANGUAGES: &[&str] = &["en", "de"];
|
||||||
const DRAWER_ANCHORS: &[&str] = &["left", "right"];
|
|
||||||
|
|
||||||
pub fn router() -> Router<AppState> {
|
pub fn router() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
@@ -434,12 +433,6 @@ async fn update_preferences(
|
|||||||
updated = true;
|
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.
|
// selected_banner: present + non-null. "" clears; otherwise must be enabled.
|
||||||
match data.get("selected_banner") {
|
match data.get("selected_banner") {
|
||||||
@@ -489,7 +482,6 @@ async fn update_preferences(
|
|||||||
"status": "preferences updated",
|
"status": "preferences updated",
|
||||||
"theme": saved.theme,
|
"theme": saved.theme,
|
||||||
"language": saved.language,
|
"language": saved.language,
|
||||||
"drawer_anchor": saved.drawer_anchor,
|
|
||||||
"selected_banner": saved.selected_banner_id,
|
"selected_banner": saved.selected_banner_id,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
@@ -763,7 +755,6 @@ async fn signup(
|
|||||||
avatar: Set(None),
|
avatar: Set(None),
|
||||||
language: Set("en".into()),
|
language: Set("en".into()),
|
||||||
selected_banner_id: Set(None),
|
selected_banner_id: Set(None),
|
||||||
drawer_anchor: Set("right".into()),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.insert(&state.db)
|
.insert(&state.db)
|
||||||
|
|||||||
@@ -758,7 +758,6 @@ pub async fn build_users(
|
|||||||
email: u.email,
|
email: u.email,
|
||||||
theme: u.theme,
|
theme: u.theme,
|
||||||
language: u.language,
|
language: u.language,
|
||||||
drawer_anchor: u.drawer_anchor,
|
|
||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ export interface AuthUser {
|
|||||||
is_superuser: boolean;
|
is_superuser: boolean;
|
||||||
theme?: string;
|
theme?: string;
|
||||||
language?: string;
|
language?: string;
|
||||||
drawer_anchor?: 'left' | 'right';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FollowingEntry {
|
export interface FollowingEntry {
|
||||||
|
|||||||
@@ -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<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useDrawerAnchor = (): UseDrawerAnchorResult => {
|
|
||||||
const { user, refetchUser } = useAuth();
|
|
||||||
const [anchor, setAnchorState] = useState<DrawerAnchor>(
|
|
||||||
(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 };
|
|
||||||
};
|
|
||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { Link, useLocation } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -94,8 +93,6 @@ const MainPage: React.FC = () => {
|
|||||||
const [autoPlay, setAutoPlay] = useState(true);
|
const [autoPlay, setAutoPlay] = useState(true);
|
||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');
|
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');
|
||||||
const muiTheme = useTheme();
|
|
||||||
const isDesktop = useMediaQuery(muiTheme.breakpoints.up('md'));
|
|
||||||
const touchStartX = useRef<number | null>(null);
|
const touchStartX = useRef<number | null>(null);
|
||||||
|
|
||||||
const query = new URLSearchParams(location.search).get('q') || '';
|
const query = new URLSearchParams(location.search).get('q') || '';
|
||||||
@@ -533,8 +530,8 @@ const MainPage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Desktop-only category browser */}
|
{/* Category browser */}
|
||||||
{isDesktop && <CategoryBrowseGrid categories={categories} />}
|
<CategoryBrowseGrid categories={categories} />
|
||||||
|
|
||||||
{/* Algorithmic discovery: personalized (members) or popular picks (visitors) */}
|
{/* Algorithmic discovery: personalized (members) or popular picks (visitors) */}
|
||||||
<MainPageRecommendations isLoggedIn={isLoggedIn} />
|
<MainPageRecommendations isLoggedIn={isLoggedIn} />
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ import {
|
|||||||
ListSubheader,
|
ListSubheader,
|
||||||
Divider,
|
Divider,
|
||||||
Grid,
|
Grid,
|
||||||
FormControlLabel,
|
|
||||||
Switch,
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
@@ -36,7 +34,6 @@ import { useTitle } from '../hooks/useTitle';
|
|||||||
import type { CatppuccinTheme } from '../theme';
|
import type { CatppuccinTheme } from '../theme';
|
||||||
import { PageShell, GlassCard, FormCard } from '../components/ui';
|
import { PageShell, GlassCard, FormCard } from '../components/ui';
|
||||||
import { useConfirm } from '../hooks/useConfirm';
|
import { useConfirm } from '../hooks/useConfirm';
|
||||||
import { useDrawerAnchor } from '../hooks/useDrawerAnchor';
|
|
||||||
|
|
||||||
interface SettingsProps {
|
interface SettingsProps {
|
||||||
themeName: CatppuccinTheme;
|
themeName: CatppuccinTheme;
|
||||||
@@ -54,7 +51,6 @@ const Settings: React.FC<SettingsProps> = ({ themeName, onThemeChange }) => {
|
|||||||
const { refetchUser } = useAuth();
|
const { refetchUser } = useAuth();
|
||||||
useTitle(t('nav.settings'));
|
useTitle(t('nav.settings'));
|
||||||
const { confirm } = useConfirm();
|
const { confirm } = useConfirm();
|
||||||
const { anchor: drawerAnchor, setAnchor: setDrawerAnchor } = useDrawerAnchor();
|
|
||||||
const { data: me } = useMe();
|
const { data: me } = useMe();
|
||||||
const { data: banners = [] } = useBannersList();
|
const { data: banners = [] } = useBannersList();
|
||||||
const updateAvatarMutation = useUpdateAvatar();
|
const updateAvatarMutation = useUpdateAvatar();
|
||||||
@@ -324,31 +320,6 @@ const Settings: React.FC<SettingsProps> = ({ themeName, onThemeChange }) => {
|
|||||||
<MenuItem value="de">Deutsch</MenuItem>
|
<MenuItem value="de">Deutsch</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<Divider sx={{ my: 2 }} />
|
|
||||||
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Switch
|
|
||||||
checked={drawerAnchor === 'left'}
|
|
||||||
onChange={(e) => setDrawerAnchor(e.target.checked ? 'left' : 'right')}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={
|
|
||||||
<Box>
|
|
||||||
<Typography variant="body1">
|
|
||||||
{t('settings.leftHandMode', { defaultValue: 'Left-hand mode' })}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="caption" color="text.secondary">
|
|
||||||
{t('settings.leftHandModeHint', {
|
|
||||||
defaultValue:
|
|
||||||
'Mobile drawer slides in from the left (better for left-handed thumb reach).',
|
|
||||||
})}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
sx={{ alignItems: 'flex-start', mt: 1 }}
|
|
||||||
/>
|
|
||||||
</GlassCard>
|
</GlassCard>
|
||||||
|
|
||||||
{/* Avatar: NOT a FormCard — file input lives outside any form */}
|
{/* Avatar: NOT a FormCard — file input lives outside any form */}
|
||||||
|
|||||||
Reference in new issue
Block a user