Public Access
refactor(frontend): lint, one session guard, one request helper, one user context
This commit is contained in:
1 parent
111ab1e200
commit
d0ea9e900d
78 files changed
+2138
-624
No files matched your search
+20
-60
@@ -1,5 +1,5 @@
|
||||
import {useEffect, useRef, useState} from 'react';
|
||||
import {useNavigate, useSearchParams} from 'react-router-dom';
|
||||
import {useSearchParams} from 'react-router-dom';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {useForm} from 'react-hook-form';
|
||||
import {zodResolver} from '@hookform/resolvers/zod';
|
||||
@@ -20,7 +20,7 @@ import FormField from '../components/FormField.jsx';
|
||||
import SteamAccountCard from '../components/SteamAccountCard.jsx';
|
||||
import {settingsSchema} from '../utils/validation.js';
|
||||
import {usePreferences} from '../context/PreferencesContext.jsx';
|
||||
import useSessionExpired from '../hooks/useSessionExpired.js';
|
||||
import {useCurrentUserContext} from '../context/CurrentUserContext.jsx';
|
||||
import useSaveAppearance from '../hooks/useSaveAppearance.js';
|
||||
import {AccentPicker, CornersPicker} from '../components/StylePickers.jsx';
|
||||
import ProfileStyleSettings from '../components/ProfileStyleSettings.jsx';
|
||||
@@ -28,8 +28,8 @@ import {normalizeProfileStyle} from '../utils/profileStyle.js';
|
||||
import useSaveProfileStyle from '../hooks/useSaveProfileStyle.js';
|
||||
import useSaveResponse from '../hooks/useSaveResponse.js';
|
||||
import {coverUrl} from '../utils/games.js';
|
||||
import {apiFetch, refreshSession} from '../utils/api.js';
|
||||
import {hasSession, markSession} from '../utils/auth.js';
|
||||
import {refreshSession} from '../utils/api.js';
|
||||
import {markSession} from '../utils/auth.js';
|
||||
import {FAVORITE_PLATFORMS, saveFavoritePlatform} from '../utils/platforms.js';
|
||||
import {LANGUAGES, saveSettings} from '../utils/settings.js';
|
||||
import {MAX_DISPLAY_NAME_LENGTH} from '../utils/limits.js';
|
||||
@@ -60,14 +60,12 @@ const formatDate = (value, locale) => {
|
||||
|
||||
export default function SettingsPage() {
|
||||
const {t, i18n} = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const expireSession = useSessionExpired();
|
||||
const {isDarkMode, setDarkMode, setLanguage, appearance} = usePreferences();
|
||||
const saveAppearance = useSaveAppearance();
|
||||
const saveStyle = useSaveProfileStyle();
|
||||
const runSave = useSaveResponse();
|
||||
|
||||
const [profile, setProfile] = useState(null);
|
||||
const {user: profile, updateUser: updateProfile, reload: reloadUser} = useCurrentUserContext();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [loadError, setLoadError] = useState(null);
|
||||
const [savingProfileStyle, setSavingProfileStyle] = useState(false);
|
||||
@@ -99,62 +97,26 @@ export default function SettingsPage() {
|
||||
}, [steamOutcome, setSearchParams, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasSession()) {
|
||||
navigate('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
const load = async () => {
|
||||
try {
|
||||
if (steamOutcome === 'linked') {
|
||||
// Refresh reads the new Steam ID from the database, so Core gets it in the token at once.
|
||||
await refreshSession().catch(() => false);
|
||||
markSession();
|
||||
}
|
||||
const response = await apiFetch(`${import.meta.env.VITE_API_URL}/api/users/me?v=1`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (response.status === 401) {
|
||||
expireSession();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
setLoadError('INFO.ERROR.LOAD_PROFILE');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setProfile(data);
|
||||
reset({displayName: data.displayName ?? ''});
|
||||
} catch (err) {
|
||||
console.error('Settings load error:', err);
|
||||
setLoadError('INFO.ERROR.NETWORK_FAILURE');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
if (steamOutcome === 'linked') {
|
||||
// Refresh reads the new Steam ID from the database, so Core gets it in the token at once.
|
||||
await refreshSession().catch(() => false);
|
||||
markSession();
|
||||
}
|
||||
const {ok, status, data} = await reloadUser();
|
||||
if (ok) reset({displayName: data.displayName ?? ''});
|
||||
else if (status !== 401) setLoadError(status === 0 ? 'INFO.ERROR.NETWORK_FAILURE' : 'INFO.ERROR.LOAD_PROFILE');
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
load();
|
||||
}, [navigate, expireSession, reset, steamOutcome]);
|
||||
}, [reloadUser, reset, steamOutcome]);
|
||||
|
||||
const persistSettings = async (send) => {
|
||||
if (!hasSession()) {
|
||||
navigate('/login');
|
||||
return {ok: false};
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await send();
|
||||
|
||||
if (response.status === 401) {
|
||||
expireSession();
|
||||
return {ok: false};
|
||||
}
|
||||
if (response.status === 401) return {ok: false};
|
||||
|
||||
if (!response.ok) {
|
||||
const data = await response.json().catch(() => ({}));
|
||||
@@ -185,7 +147,7 @@ export default function SettingsPage() {
|
||||
const handleFavoritePlatformChange = async (value) => {
|
||||
const platform = value === NO_FAVORITE ? null : value;
|
||||
if (await runSave(() => saveFavoritePlatform(platform))) {
|
||||
setProfile((p) => ({...p, settings: {...p.settings, favoritePlatform: platform}}));
|
||||
updateProfile((p) => ({...p, settings: {...p.settings, favoritePlatform: platform}}));
|
||||
toast.success(t('SETTINGS.SAVED'));
|
||||
}
|
||||
};
|
||||
@@ -194,14 +156,14 @@ export default function SettingsPage() {
|
||||
const next = !profile?.settings?.publicProfile;
|
||||
const result = await persistSettings(() => saveSettings({publicProfile: next}));
|
||||
if (result.ok) {
|
||||
setProfile((p) => ({...p, settings: {...p.settings, publicProfile: next}}));
|
||||
updateProfile((p) => ({...p, settings: {...p.settings, publicProfile: next}}));
|
||||
}
|
||||
};
|
||||
|
||||
// Shown at once; the pickers wait for the answer, so the saves reach Auth one after the other.
|
||||
const handleProfileStyleChange = async (changes) => {
|
||||
const previous = normalizeProfileStyle(profile?.settings?.profileStyle);
|
||||
const showProfileStyle = (next) => setProfile((p) => ({...p, settings: {...p.settings, profileStyle: next}}));
|
||||
const showProfileStyle = (next) => updateProfile((p) => ({...p, settings: {...p.settings, profileStyle: next}}));
|
||||
showProfileStyle({...previous, ...changes});
|
||||
setSavingProfileStyle(true);
|
||||
showProfileStyle(await saveStyle(changes, previous) ?? previous);
|
||||
@@ -222,9 +184,7 @@ export default function SettingsPage() {
|
||||
const publicProfile = !!profile?.settings?.publicProfile && !!values.displayName;
|
||||
const result = await persistSettings(() => saveSettings({displayName: values.displayName, publicProfile}));
|
||||
if (result.ok) {
|
||||
setProfile((p) => (p ? {
|
||||
...p, displayName: values.displayName, settings: {...p.settings, publicProfile},
|
||||
} : p));
|
||||
updateProfile((p) => ({...p, displayName: values.displayName, settings: {...p.settings, publicProfile}}));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -382,7 +342,7 @@ export default function SettingsPage() {
|
||||
</Card>
|
||||
|
||||
<SteamAccountCard steamId={profile?.steamId} verified={!!profile?.steamIdVerified}
|
||||
onUnlinked={() => setProfile((p) => ({...p, steamId: null, steamIdVerified: false}))}/>
|
||||
onUnlinked={() => updateProfile((p) => ({...p, steamId: null, steamIdVerified: false}))}/>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
|
||||
Reference in new issue
Block a user