Files
dotcom-arcade/docs/04-frontend/custom-hooks.md
T
Darkraise c8083eb6b5 feat: add activity analytics dashboard and comprehensive security/accessibility improvements
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.
2026-01-25 13:18:04 +07:00

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
    }
  }
});

Further Reading