Files
branderson 4130adfe58 docs: document Dotcom Arcade, streaming, proxy sign-in and saves
A new README for the combined project with setup from the Flashpoint
Ultimate torrent. New feature docs for streaming, proxy sign-in and
game saves; deployment, upgrade and streaming READMEs updated for the
single compose file; the project renamed throughout.
2026-10-13 19:31:06 -07:00

7.3 KiB

Custom Hooks

Comprehensive documentation of all custom React hooks in the Dotcom Arcade frontend.

Overview

The application uses 30 custom hooks organized by functionality:

  • Data Fetching - useGames, usePlaylists, useUserPlaylists, useUsers, useRoles, useActivities, useFavorites
  • Authentication - useAuth, usePublicSettings
  • UI - useDebounce, useSwipeGesture, useMountEffect, useToast, useDateTimeFormat, useMediaQuery, useFeatureFlags
  • Game Data - useFilterOptions, useFilterDropdowns, useTags, usePlatforms, useStatistics, useDownload, useMostPlayedGames, useRecentGames
  • Play Tracking - usePlayTracking, useActivityFilters
  • Community - useCommunityPlaylists
  • Shared Access - useSharedAccessToken, useSharedPlaylistAccess, useDomains

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

useDomains

Domain management hooks for admin domain settings.

Location: frontend/src/hooks/useDomains.ts

// Fetch all domains (admin only)
const { data: domains } = useDomains(enabled?: boolean);

// Mutations
const addDomain = useAddDomain();       // addDomain.mutate('play.example.com')
const deleteDomain = useDeleteDomain(); // deleteDomain.mutate(domainId)
const setDefault = useSetDefaultDomain(); // setDefault.mutate(domainId) - optimistic update

// Utility: build share URL from hostname + token
import { buildShareUrl } from '@/hooks/useDomains';
const url = buildShareUrl('play.example.com', shareToken);
// => "https://play.example.com/playlists/shared/{token}"
const fallback = buildShareUrl(null, shareToken);
// => "{window.location.origin}/playlists/shared/{token}"

All mutations automatically invalidate the ['system-settings', 'public'] cache so the default domain updates across the app.

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