mirror of
https://github.com/Novattz/creamlinux-installer.git
synced 2026-01-24 20:32:51 -05:00
index & hook #89
This commit is contained in:
66
src/App.tsx
66
src/App.tsx
@@ -1,13 +1,27 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
import { useAppContext } from '@/contexts/useAppContext'
|
import { useAppContext } from '@/contexts/useAppContext'
|
||||||
import { useAppLogic } from '@/hooks'
|
import { useAppLogic, useConflictDetection } from '@/hooks'
|
||||||
import './styles/main.scss'
|
import './styles/main.scss'
|
||||||
|
|
||||||
// Layout components
|
// Layout components
|
||||||
import { Header, Sidebar, InitialLoadingScreen, ErrorBoundary, UpdateScreen, AnimatedBackground } from '@/components/layout'
|
import {
|
||||||
|
Header,
|
||||||
|
Sidebar,
|
||||||
|
InitialLoadingScreen,
|
||||||
|
ErrorBoundary,
|
||||||
|
UpdateScreen,
|
||||||
|
AnimatedBackground,
|
||||||
|
} from '@/components/layout'
|
||||||
|
|
||||||
// Dialog components
|
// Dialog components
|
||||||
import { ProgressDialog, DlcSelectionDialog, SettingsDialog } from '@/components/dialogs'
|
import {
|
||||||
|
ProgressDialog,
|
||||||
|
DlcSelectionDialog,
|
||||||
|
SettingsDialog,
|
||||||
|
ConflictDialog,
|
||||||
|
ReminderDialog,
|
||||||
|
} from '@/components/dialogs'
|
||||||
|
|
||||||
// Game components
|
// Game components
|
||||||
import { GameList } from '@/components/games'
|
import { GameList } from '@/components/games'
|
||||||
@@ -17,6 +31,7 @@ import { GameList } from '@/components/games'
|
|||||||
*/
|
*/
|
||||||
function App() {
|
function App() {
|
||||||
const [updateComplete, setUpdateComplete] = useState(false)
|
const [updateComplete, setUpdateComplete] = useState(false)
|
||||||
|
|
||||||
// Get application logic from hook
|
// Get application logic from hook
|
||||||
const {
|
const {
|
||||||
filter,
|
filter,
|
||||||
@@ -33,6 +48,7 @@ function App() {
|
|||||||
|
|
||||||
// Get action handlers from context
|
// Get action handlers from context
|
||||||
const {
|
const {
|
||||||
|
games,
|
||||||
dlcDialog,
|
dlcDialog,
|
||||||
handleDlcDialogClose,
|
handleDlcDialogClose,
|
||||||
handleProgressDialogClose,
|
handleProgressDialogClose,
|
||||||
@@ -45,8 +61,30 @@ function App() {
|
|||||||
handleSettingsOpen,
|
handleSettingsOpen,
|
||||||
handleSettingsClose,
|
handleSettingsClose,
|
||||||
handleSmokeAPISettingsOpen,
|
handleSmokeAPISettingsOpen,
|
||||||
|
showToast,
|
||||||
} = useAppContext()
|
} = useAppContext()
|
||||||
|
|
||||||
|
// Conflict detection
|
||||||
|
const { currentConflict, showReminder, resolveConflict, closeReminder } =
|
||||||
|
useConflictDetection(games)
|
||||||
|
|
||||||
|
// Handle conflict resolution
|
||||||
|
const handleConflictResolve = async () => {
|
||||||
|
const resolution = resolveConflict()
|
||||||
|
if (!resolution) return
|
||||||
|
|
||||||
|
// Always remove files - use the special conflict resolution command
|
||||||
|
try {
|
||||||
|
await invoke('resolve_platform_conflict', {
|
||||||
|
gameId: resolution.gameId,
|
||||||
|
conflictType: resolution.conflictType,
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error resolving conflict:', error)
|
||||||
|
showToast(`Failed to resolve conflict: ${error}`, 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Show update screen first
|
// Show update screen first
|
||||||
if (!updateComplete) {
|
if (!updateComplete) {
|
||||||
return <UpdateScreen onComplete={() => setUpdateComplete(true)} />
|
return <UpdateScreen onComplete={() => setUpdateComplete(true)} />
|
||||||
@@ -73,7 +111,11 @@ function App() {
|
|||||||
|
|
||||||
<div className="main-content">
|
<div className="main-content">
|
||||||
{/* Sidebar for filtering */}
|
{/* Sidebar for filtering */}
|
||||||
<Sidebar setFilter={setFilter} currentFilter={filter} onSettingsClick={handleSettingsOpen} />
|
<Sidebar
|
||||||
|
setFilter={setFilter}
|
||||||
|
currentFilter={filter}
|
||||||
|
onSettingsClick={handleSettingsOpen}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Show error or game list */}
|
{/* Show error or game list */}
|
||||||
{error ? (
|
{error ? (
|
||||||
@@ -123,10 +165,20 @@ function App() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Settings Dialog */}
|
{/* Settings Dialog */}
|
||||||
<SettingsDialog
|
<SettingsDialog visible={settingsDialog.visible} onClose={handleSettingsClose} />
|
||||||
visible ={settingsDialog.visible}
|
|
||||||
onClose={handleSettingsClose}
|
{/* Conflict Detection Dialog */}
|
||||||
|
{currentConflict && (
|
||||||
|
<ConflictDialog
|
||||||
|
visible={true}
|
||||||
|
gameTitle={currentConflict.gameTitle}
|
||||||
|
conflictType={currentConflict.type}
|
||||||
|
onConfirm={handleConflictResolve}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Steam Launch Options Reminder */}
|
||||||
|
<ReminderDialog visible={showReminder} onClose={closeReminder} />
|
||||||
</div>
|
</div>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ export { default as ProgressDialog } from './ProgressDialog'
|
|||||||
export { default as DlcSelectionDialog } from './DlcSelectionDialog'
|
export { default as DlcSelectionDialog } from './DlcSelectionDialog'
|
||||||
export { default as SettingsDialog } from './SettingsDialog'
|
export { default as SettingsDialog } from './SettingsDialog'
|
||||||
export { default as SmokeAPISettingsDialog } from './SmokeAPISettingsDialog'
|
export { default as SmokeAPISettingsDialog } from './SmokeAPISettingsDialog'
|
||||||
|
export { default as ConflictDialog } from './ConflictDialog'
|
||||||
|
export { default as ReminderDialog } from './ReminderDialog'
|
||||||
|
|
||||||
// Export types
|
// Export types
|
||||||
export type { DialogProps } from './Dialog'
|
export type { DialogProps } from './Dialog'
|
||||||
@@ -17,3 +19,5 @@ export type { DialogFooterProps } from './DialogFooter'
|
|||||||
export type { DialogActionsProps } from './DialogActions'
|
export type { DialogActionsProps } from './DialogActions'
|
||||||
export type { ProgressDialogProps, InstallationInstructions } from './ProgressDialog'
|
export type { ProgressDialogProps, InstallationInstructions } from './ProgressDialog'
|
||||||
export type { DlcSelectionDialogProps } from './DlcSelectionDialog'
|
export type { DlcSelectionDialogProps } from './DlcSelectionDialog'
|
||||||
|
export type { ConflictDialogProps } from './ConflictDialog'
|
||||||
|
export type { ReminderDialogProps } from './ReminderDialog'
|
||||||
@@ -4,7 +4,9 @@ export { useDlcManager } from './useDlcManager'
|
|||||||
export { useGameActions } from './useGameActions'
|
export { useGameActions } from './useGameActions'
|
||||||
export { useToasts } from './useToasts'
|
export { useToasts } from './useToasts'
|
||||||
export { useAppLogic } from './useAppLogic'
|
export { useAppLogic } from './useAppLogic'
|
||||||
|
export { useConflictDetection } from './useConflictDetection'
|
||||||
|
|
||||||
// Export types
|
// Export types
|
||||||
export type { ToastType, Toast, ToastOptions } from './useToasts'
|
export type { ToastType, Toast, ToastOptions } from './useToasts'
|
||||||
export type { DlcDialogState } from './useDlcManager'
|
export type { DlcDialogState } from './useDlcManager'
|
||||||
|
export type { Conflict, ConflictResolution } from './useConflictDetection'
|
||||||
123
src/hooks/useConflictDetection.ts
Normal file
123
src/hooks/useConflictDetection.ts
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import { useState, useEffect, useCallback } from 'react'
|
||||||
|
import { Game } from '@/types'
|
||||||
|
|
||||||
|
export interface Conflict {
|
||||||
|
gameId: string
|
||||||
|
gameTitle: string
|
||||||
|
type: 'cream-to-proton' | 'smoke-to-native'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConflictResolution {
|
||||||
|
gameId: string
|
||||||
|
removeFiles: boolean
|
||||||
|
conflictType: 'cream-to-proton' | 'smoke-to-native'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for detecting platform conflicts
|
||||||
|
* Identifies when unlocker files exist for the wrong platform
|
||||||
|
*/
|
||||||
|
export function useConflictDetection(games: Game[]) {
|
||||||
|
const [conflicts, setConflicts] = useState<Conflict[]>([])
|
||||||
|
const [currentConflict, setCurrentConflict] = useState<Conflict | null>(null)
|
||||||
|
const [showReminder, setShowReminder] = useState(false)
|
||||||
|
const [isProcessing, setIsProcessing] = useState(false)
|
||||||
|
const [resolvedConflicts, setResolvedConflicts] = useState<Set<string>>(new Set())
|
||||||
|
|
||||||
|
// Detect conflicts whenever games change
|
||||||
|
useEffect(() => {
|
||||||
|
const detectedConflicts: Conflict[] = []
|
||||||
|
|
||||||
|
games.forEach((game) => {
|
||||||
|
// Skip if we've already resolved a conflict for this game
|
||||||
|
if (resolvedConflicts.has(game.id)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conflict 1: CreamLinux installed but game is now Proton
|
||||||
|
if (!game.native && game.cream_installed) {
|
||||||
|
detectedConflicts.push({
|
||||||
|
gameId: game.id,
|
||||||
|
gameTitle: game.title,
|
||||||
|
type: 'cream-to-proton',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conflict 2: SmokeAPI installed but game is now Native
|
||||||
|
if (game.native && game.smoke_installed) {
|
||||||
|
detectedConflicts.push({
|
||||||
|
gameId: game.id,
|
||||||
|
gameTitle: game.title,
|
||||||
|
type: 'smoke-to-native',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setConflicts(detectedConflicts)
|
||||||
|
|
||||||
|
// Show the first conflict if we have any and not currently processing
|
||||||
|
if (detectedConflicts.length > 0 && !currentConflict && !isProcessing) {
|
||||||
|
setCurrentConflict(detectedConflicts[0])
|
||||||
|
}
|
||||||
|
}, [games, currentConflict, isProcessing, resolvedConflicts])
|
||||||
|
|
||||||
|
// Handle conflict resolution
|
||||||
|
const resolveConflict = useCallback((): ConflictResolution | null => {
|
||||||
|
if (!currentConflict || isProcessing) return null
|
||||||
|
|
||||||
|
setIsProcessing(true)
|
||||||
|
|
||||||
|
const resolution: ConflictResolution = {
|
||||||
|
gameId: currentConflict.gameId,
|
||||||
|
removeFiles: true, // Always remove files
|
||||||
|
conflictType: currentConflict.type,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark this game as resolved so we don't re-detect the conflict
|
||||||
|
setResolvedConflicts((prev) => new Set(prev).add(currentConflict.gameId))
|
||||||
|
|
||||||
|
// Remove this conflict from the list
|
||||||
|
const remainingConflicts = conflicts.filter((c) => c.gameId !== currentConflict.gameId)
|
||||||
|
setConflicts(remainingConflicts)
|
||||||
|
|
||||||
|
// Close current conflict dialog immediately
|
||||||
|
setCurrentConflict(null)
|
||||||
|
|
||||||
|
// Determine what to show next based on conflict type
|
||||||
|
if (resolution.conflictType === 'cream-to-proton') {
|
||||||
|
// CreamLinux removal - show reminder after delay
|
||||||
|
setTimeout(() => {
|
||||||
|
setShowReminder(true)
|
||||||
|
setIsProcessing(false)
|
||||||
|
}, 100)
|
||||||
|
} else {
|
||||||
|
// SmokeAPI removal - no reminder, just show next conflict or finish
|
||||||
|
setTimeout(() => {
|
||||||
|
if (remainingConflicts.length > 0) {
|
||||||
|
setCurrentConflict(remainingConflicts[0])
|
||||||
|
}
|
||||||
|
setIsProcessing(false)
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolution
|
||||||
|
}, [currentConflict, conflicts, isProcessing])
|
||||||
|
|
||||||
|
// Close reminder dialog
|
||||||
|
const closeReminder = useCallback(() => {
|
||||||
|
setShowReminder(false)
|
||||||
|
|
||||||
|
// After closing reminder, check if there are more conflicts
|
||||||
|
if (conflicts.length > 0) {
|
||||||
|
setCurrentConflict(conflicts[0])
|
||||||
|
}
|
||||||
|
}, [conflicts])
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentConflict,
|
||||||
|
showReminder,
|
||||||
|
resolveConflict,
|
||||||
|
closeReminder,
|
||||||
|
hasConflicts: conflicts.length > 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user