Initial changes

This commit is contained in:
Tickbase
2025-05-18 08:06:56 +02:00
parent 19087c00da
commit 0be15f83e7
82 changed files with 4636 additions and 3237 deletions

View File

@@ -0,0 +1,56 @@
import { createContext } from 'react'
import { Game, DlcInfo } from '@/types'
import { ActionType } from '@/components/buttons/ActionButton'
// Types for context sub-components
export interface InstallationInstructions {
type: string;
command: string;
game_title: string;
dlc_count?: number;
}
export interface DlcDialogState {
visible: boolean;
gameId: string;
gameTitle: string;
dlcs: DlcInfo[];
isLoading: boolean;
isEditMode: boolean;
progress: number;
timeLeft?: string;
}
export interface ProgressDialogState {
visible: boolean;
title: string;
message: string;
progress: number;
showInstructions: boolean;
instructions?: InstallationInstructions;
}
// Define the context type
export interface AppContextType {
// Game state
games: Game[];
isLoading: boolean;
error: string | null;
loadGames: () => Promise<boolean>;
// DLC management
dlcDialog: DlcDialogState;
handleGameEdit: (gameId: string) => void;
handleDlcDialogClose: () => void;
// Game actions
progressDialog: ProgressDialogState;
handleGameAction: (gameId: string, action: ActionType) => Promise<void>;
handleDlcConfirm: (selectedDlcs: DlcInfo[]) => void;
// Toast notifications
showToast: (message: string, type: 'success' | 'error' | 'warning' | 'info', options?: Record<string, unknown>) => void;
}
// Create the context with a default value
export const AppContext = createContext<AppContextType | undefined>(undefined);

View File

@@ -0,0 +1,171 @@
import { ReactNode } from 'react'
import { AppContext, AppContextType } from './AppContext'
import { useGames, useDlcManager, useGameActions, useToasts } from '@/hooks'
import { DlcInfo } from '@/types'
import { ActionType } from '@/components/buttons/ActionButton'
import { ToastContainer } from '@/components/notifications'
// Context provider component
interface AppProviderProps {
children: ReactNode;
}
/**
* Primary application context provider
* Manages global state and provides methods for component interaction
*/
export const AppProvider = ({ children }: AppProviderProps) => {
// Use our custom hooks
const {
games,
isLoading,
error,
loadGames,
setGames,
} = useGames()
const {
dlcDialog,
setDlcDialog,
handleDlcDialogClose: closeDlcDialog,
streamGameDlcs,
} = useDlcManager()
const {
progressDialog,
handleGameAction: executeGameAction,
handleDlcConfirm: executeDlcConfirm,
} = useGameActions()
const {
toasts,
removeToast,
success,
error: showError,
warning,
info
} = useToasts()
// Combined handler for game edit
const handleGameEdit = async (gameId: string) => {
const game = games.find(g => g.id === gameId)
if (!game || !game.cream_installed) {
showError("Cannot edit game: not found or CreamLinux not installed")
return
}
try {
await streamGameDlcs(gameId)
setDlcDialog({
...dlcDialog,
visible: true,
gameId,
gameTitle: game.title,
isLoading: true,
isEditMode: true,
})
} catch (error) {
showError(`Failed to load DLCs: ${error}`)
}
}
// Enhanced game action handler with proper error reporting
const handleGameAction = async (gameId: string, action: ActionType) => {
const game = games.find(g => g.id === gameId)
if (!game) {
showError("Game not found")
return
}
// Mark game as installing
setGames(prevGames =>
prevGames.map(g => g.id === gameId ? {...g, installing: true} : g)
)
try {
await executeGameAction(gameId, action, games)
// Show success message
if (action.includes('install')) {
success(`Successfully installed ${action.includes('cream') ? 'CreamLinux' : 'SmokeAPI'} for ${game.title}`)
} else {
success(`Successfully uninstalled ${action.includes('cream') ? 'CreamLinux' : 'SmokeAPI'} from ${game.title}`)
}
} catch (error) {
showError(`Action failed: ${error}`)
} finally {
// Reset installing state
setGames(prevGames =>
prevGames.map(g => g.id === gameId ? {...g, installing: false} : g)
)
}
}
// DLC confirmation wrapper
const handleDlcConfirm = (selectedDlcs: DlcInfo[]) => {
closeDlcDialog()
const { gameId, isEditMode } = dlcDialog
// Update game state to show it's installing
setGames(prevGames =>
prevGames.map(g => g.id === gameId ? { ...g, installing: true } : g)
)
executeDlcConfirm(selectedDlcs, gameId, isEditMode, games)
.then(() => {
success(isEditMode
? "DLC configuration updated successfully"
: "CreamLinux installed with selected DLCs")
})
.catch(error => {
showError(`DLC operation failed: ${error}`)
})
.finally(() => {
// Reset installing state
setGames(prevGames =>
prevGames.map(g => g.id === gameId ? { ...g, installing: false } : g)
)
})
}
// Generic toast show function
const showToast = (message: string, type: 'success' | 'error' | 'warning' | 'info', options = {}) => {
switch (type) {
case 'success': success(message, options); break;
case 'error': showError(message, options); break;
case 'warning': warning(message, options); break;
case 'info': info(message, options); break;
}
}
// Provide all the values to the context
const contextValue: AppContextType = {
// Game state
games,
isLoading,
error,
loadGames,
// DLC management
dlcDialog,
handleGameEdit,
handleDlcDialogClose: closeDlcDialog,
// Game actions
progressDialog,
handleGameAction,
handleDlcConfirm,
// Toast notifications
showToast,
}
return (
<AppContext.Provider value={contextValue}>
{children}
<ToastContainer toasts={toasts} onDismiss={removeToast} />
</AppContext.Provider>
)
}

3
src/contexts/index.ts Normal file
View File

@@ -0,0 +1,3 @@
export * from './AppContext';
export * from './AppProvider';
export * from './useAppContext';

View File

@@ -0,0 +1,16 @@
import { useContext } from 'react'
import { AppContext, AppContextType } from './AppContext'
/**
* Custom hook to use the application context
* Ensures proper error handling if used outside of AppProvider
*/
export const useAppContext = (): AppContextType => {
const context = useContext(AppContext)
if (context === undefined) {
throw new Error('useAppContext must be used within an AppProvider')
}
return context
}