Implement activity analytics dashboard with real-time stats, charts, and user leaderboard. Add automated Ruffle update job for Flash emulator maintenance. Standardize datetime formatting across frontend/backend with ISO 8601 and user preferences. Enhance security with comprehensive input validation, rate limiting, and CSRF protection. Improve accessibility with ARIA labels, keyboard navigation, and screen reader support. Fix multiple security vulnerabilities including XSS, SQL injection risks, and session management issues.
6.1 KiB
Custom Hooks
Comprehensive documentation of all custom React hooks in the Flashpoint Web frontend.
Overview
The application uses 18+ custom hooks organized by functionality:
- Data Fetching - useGames, usePlaylists, useUsers, useRoles, useActivities
- Authentication - useAuth
- UI - useDebounce, useSwipeGesture, useMountEffect, useToast, useDateTimeFormat
- Game Data - useFilterOptions, useTags, usePlatforms, useStatistics, useDownload
- Play Tracking - usePlayTracking
- Community - useCommunityPlaylists
Data Fetching Hooks
useGames
Game search and retrieval hooks.
Location: frontend/src/hooks/useGames.ts
// Search games with filters
const { data, isLoading, error } = useGames(filters: GameFilters);
// Get single game by ID
const { data: game } = useGame(id: string);
// Get related games
const { data: related } = useRelatedGames(id: string, limit = 10);
// Get random game
const { data: random } = useRandomGame(library?: string);
// Get launch data for playing
const { data: launchData } = useGameLaunchData(id: string);
Example:
function BrowseView() {
const [filters, setFilters] = useState<GameFilters>({
platform: 'Flash',
search: '',
page: 1,
limit: 50
});
const { data, isLoading, error } = useGames(filters);
if (isLoading) return <GameGridSkeleton />;
if (error) return <ErrorMessage error={error} />;
return <GameGrid games={data.games} total={data.total} />;
}
usePlaylists
Playlist management hooks.
Location: frontend/src/hooks/usePlaylists.ts
const { data: playlists } = usePlaylists();
const { data: playlist } = usePlaylist(id: string);
const createMutation = useCreatePlaylist();
const addGamesMutation = useAddGamesToPlaylist();
const removeGamesMutation = useRemoveGamesFromPlaylist();
const deleteMutation = useDeletePlaylist();
Authentication Hook
useAuth
Authentication context hook with login/logout functions.
Location: frontend/src/hooks/useAuth.ts
const { login, logout, register } = useAuth();
// Login
await login({ username: 'user', password: 'pass' });
// Register
await register({ username: 'newuser', email: 'email@example.com', password: 'pass' });
// Logout
await logout();
Utility Hooks
useDebounce
Debounce value changes for search inputs.
const [search, setSearch] = useState('');
const debouncedSearch = useDebounce(search, 500);
useEffect(() => {
// Only triggers 500ms after user stops typing
fetchGames({ search: debouncedSearch });
}, [debouncedSearch]);
useMountEffect
Run effect only on component mount.
useMountEffect(() => {
console.log('Component mounted');
fetchInitialData();
});
useSwipeGesture
Detect swipe gestures for mobile.
const ref = useSwipeGesture<HTMLDivElement>({
onSwipeLeft: () => console.log('Swiped left'),
onSwipeRight: () => console.log('Swiped right'),
minSwipeDistance: 50
});
return <div ref={ref}>Swipeable content</div>;
useDateTimeFormat
Date and time formatting hook with user-configurable formats.
Location: frontend/src/hooks/useDateTimeFormat.ts
Purpose: Provides consistent date/time formatting across the application using user-selected format preferences from system settings.
const {
dateFormat, // Current date format string (e.g., 'MM/dd/yyyy')
timeFormat, // Current time format string (e.g., 'hh:mm a')
formatDate, // Function to format dates
formatTime, // Function to format times
formatDateTime // Function to format date + time
} = useDateTimeFormat();
Available Date Formats:
MM/dd/yyyy- US format (01/24/2026)dd/MM/yyyy- European format (24/01/2026)yyyy-MM-dd- ISO 8601 format (2026-01-24)MMM dd, yyyy- Short month (Jan 24, 2026)MMMM dd, yyyy- Full month (January 24, 2026)dd MMM yyyy- Day-first short month (24 Jan 2026)dd MMMM yyyy- Day-first full month (24 January 2026)
Available Time Formats:
hh:mm a- 12-hour with AM/PM (02:30 PM)HH:mm- 24-hour (14:30)hh:mm:ss a- 12-hour with seconds (02:30:45 PM)HH:mm:ss- 24-hour with seconds (14:30:45)
Example Usage:
function ActivityTable() {
const { formatDateTime } = useDateTimeFormat();
return (
<Table>
{activities.map(activity => (
<TableRow key={activity.id}>
<TableCell>{formatDateTime(activity.createdAt)}</TableCell>
</TableRow>
))}
</Table>
);
}
With FormattedDate Component:
import { FormattedDate } from '@/components/common/FormattedDate';
function GameStats() {
return (
<div>
<p>First played: <FormattedDate date={stats.firstPlayAt} type="date" /></p>
<p>Last played: <FormattedDate date={stats.lastPlayAt} type="datetime" /></p>
</div>
);
}
Features:
- Automatically fetches user's format preferences from system settings
- Caches settings with 5-minute staleTime
- Handles Date objects, ISO strings, and timestamps
- Falls back to defaults if settings not loaded
- Uses date-fns for reliable formatting
See Also: Date & Time Formatting Feature
useFilterOptions
Fetch available filter options (platforms, tags, etc.).
const { data: options, isLoading } = useFilterOptions();
// options = { platforms: [...], tags: [...], playModes: [...] }
Play Tracking Hook
usePlayTracking
Track game play sessions.
const { startSession, endSession, isPlaying } = usePlayTracking();
// Start playing
await startSession(gameId, gameTitle);
// End playing
await endSession();
Query Configuration
All hooks use TanStack Query with these defaults:
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
cacheTime: 10 * 60 * 1000, // 10 minutes
refetchOnWindowFocus: false,
retry: 1
}
}
});