mirror of
https://github.com/Novattz/creamlinux-installer.git
synced 2026-01-24 20:32:51 -05:00
dlc dialog
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect, useCallback } from 'react'
|
||||||
import Dialog from './Dialog'
|
import Dialog from './Dialog'
|
||||||
import DialogHeader from './DialogHeader'
|
import DialogHeader from './DialogHeader'
|
||||||
import DialogBody from './DialogBody'
|
import DialogBody from './DialogBody'
|
||||||
@@ -22,6 +22,7 @@ export interface DlcSelectionDialogProps {
|
|||||||
/**
|
/**
|
||||||
* DLC Selection Dialog component
|
* DLC Selection Dialog component
|
||||||
* Allows users to select which DLCs they want to enable
|
* Allows users to select which DLCs they want to enable
|
||||||
|
* Works for both initial installation and editing existing configurations
|
||||||
*/
|
*/
|
||||||
const DlcSelectionDialog = ({
|
const DlcSelectionDialog = ({
|
||||||
visible,
|
visible,
|
||||||
@@ -34,6 +35,7 @@ const DlcSelectionDialog = ({
|
|||||||
loadingProgress = 0,
|
loadingProgress = 0,
|
||||||
estimatedTimeLeft = '',
|
estimatedTimeLeft = '',
|
||||||
}: DlcSelectionDialogProps) => {
|
}: DlcSelectionDialogProps) => {
|
||||||
|
// State for DLC management
|
||||||
const [selectedDlcs, setSelectedDlcs] = useState<DlcInfo[]>([])
|
const [selectedDlcs, setSelectedDlcs] = useState<DlcInfo[]>([])
|
||||||
const [searchQuery, setSearchQuery] = useState('')
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
const [selectAll, setSelectAll] = useState(true)
|
const [selectAll, setSelectAll] = useState(true)
|
||||||
@@ -41,17 +43,29 @@ const DlcSelectionDialog = ({
|
|||||||
|
|
||||||
// Initialize selected DLCs when DLC list changes
|
// Initialize selected DLCs when DLC list changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dlcs.length > 0 && !initialized) {
|
if (dlcs.length > 0) {
|
||||||
|
if (!initialized) {
|
||||||
|
// Initial setup - preserve the enabled state from incoming DLCs
|
||||||
setSelectedDlcs(dlcs)
|
setSelectedDlcs(dlcs)
|
||||||
|
|
||||||
// Determine initial selectAll state based on if all DLCs are enabled
|
// Determine initial selectAll state based on if all DLCs are enabled
|
||||||
const allSelected = dlcs.every((dlc) => dlc.enabled)
|
const allSelected = dlcs.every((dlc) => dlc.enabled)
|
||||||
setSelectAll(allSelected)
|
setSelectAll(allSelected)
|
||||||
|
|
||||||
// Mark as initialized so we don't reset selections on subsequent DLC additions
|
// Mark as initialized to avoid resetting selections on subsequent updates
|
||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
|
} else {
|
||||||
|
// Find new DLCs that aren't in our current selection
|
||||||
|
const currentAppIds = new Set(selectedDlcs.map((dlc) => dlc.appid))
|
||||||
|
const newDlcs = dlcs.filter((dlc) => !currentAppIds.has(dlc.appid))
|
||||||
|
|
||||||
|
// If we found new DLCs, add them to our selection
|
||||||
|
if (newDlcs.length > 0) {
|
||||||
|
setSelectedDlcs((prev) => [...prev, ...newDlcs])
|
||||||
}
|
}
|
||||||
}, [dlcs, initialized])
|
}
|
||||||
|
}
|
||||||
|
}, [dlcs, selectedDlcs, initialized])
|
||||||
|
|
||||||
// Memoize filtered DLCs to avoid unnecessary recalculations
|
// Memoize filtered DLCs to avoid unnecessary recalculations
|
||||||
const filteredDlcs = React.useMemo(() => {
|
const filteredDlcs = React.useMemo(() => {
|
||||||
@@ -65,33 +79,22 @@ const DlcSelectionDialog = ({
|
|||||||
}, [selectedDlcs, searchQuery])
|
}, [selectedDlcs, searchQuery])
|
||||||
|
|
||||||
// Update DLC selection status
|
// Update DLC selection status
|
||||||
const handleToggleDlc = (appid: string) => {
|
const handleToggleDlc = useCallback((appid: string) => {
|
||||||
setSelectedDlcs((prev) =>
|
setSelectedDlcs((prev) =>
|
||||||
prev.map((dlc) => (dlc.appid === appid ? { ...dlc, enabled: !dlc.enabled } : dlc))
|
prev.map((dlc) => (dlc.appid === appid ? { ...dlc, enabled: !dlc.enabled } : dlc))
|
||||||
)
|
)
|
||||||
}
|
}, [])
|
||||||
|
|
||||||
// Update selectAll state when individual DLC selections change
|
// Update selectAll state when individual DLC selections change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (selectedDlcs.length > 0) {
|
||||||
const allSelected = selectedDlcs.every((dlc) => dlc.enabled)
|
const allSelected = selectedDlcs.every((dlc) => dlc.enabled)
|
||||||
setSelectAll(allSelected)
|
setSelectAll(allSelected)
|
||||||
|
}
|
||||||
}, [selectedDlcs])
|
}, [selectedDlcs])
|
||||||
|
|
||||||
// Handle new DLCs being added while dialog is already open
|
// Toggle all DLCs at once
|
||||||
useEffect(() => {
|
const handleToggleSelectAll = useCallback(() => {
|
||||||
if (initialized && dlcs.length > selectedDlcs.length) {
|
|
||||||
// Find new DLCs that aren't in our current selection
|
|
||||||
const currentAppIds = new Set(selectedDlcs.map((dlc) => dlc.appid))
|
|
||||||
const newDlcs = dlcs.filter((dlc) => !currentAppIds.has(dlc.appid))
|
|
||||||
|
|
||||||
// Add new DLCs to our selection, maintaining their enabled state
|
|
||||||
if (newDlcs.length > 0) {
|
|
||||||
setSelectedDlcs((prev) => [...prev, ...newDlcs])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [dlcs, selectedDlcs, initialized])
|
|
||||||
|
|
||||||
const handleToggleSelectAll = () => {
|
|
||||||
const newSelectAllState = !selectAll
|
const newSelectAllState = !selectAll
|
||||||
setSelectAll(newSelectAllState)
|
setSelectAll(newSelectAllState)
|
||||||
|
|
||||||
@@ -101,15 +104,29 @@ const DlcSelectionDialog = ({
|
|||||||
enabled: newSelectAllState,
|
enabled: newSelectAllState,
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
}
|
}, [selectAll])
|
||||||
|
|
||||||
const handleConfirm = () => {
|
// Submit selected DLCs to parent component
|
||||||
|
const handleConfirm = useCallback(() => {
|
||||||
onConfirm(selectedDlcs)
|
onConfirm(selectedDlcs)
|
||||||
|
}, [onConfirm, selectedDlcs])
|
||||||
|
|
||||||
|
// Reset dialog state when it opens or closes
|
||||||
|
useEffect(() => {
|
||||||
|
if (!visible) {
|
||||||
|
setInitialized(false)
|
||||||
|
setSelectedDlcs([])
|
||||||
|
setSearchQuery('')
|
||||||
}
|
}
|
||||||
|
}, [visible])
|
||||||
|
|
||||||
// Count selected DLCs
|
// Count selected DLCs
|
||||||
const selectedCount = selectedDlcs.filter((dlc) => dlc.enabled).length
|
const selectedCount = selectedDlcs.filter((dlc) => dlc.enabled).length
|
||||||
|
|
||||||
|
// Format dialog title and messages based on mode
|
||||||
|
const dialogTitle = isEditMode ? 'Edit DLCs' : 'Select DLCs to Enable'
|
||||||
|
const actionButtonText = isEditMode ? 'Save Changes' : 'Install with Selected DLCs'
|
||||||
|
|
||||||
// Format loading message to show total number of DLCs found
|
// Format loading message to show total number of DLCs found
|
||||||
const getLoadingInfoText = () => {
|
const getLoadingInfoText = () => {
|
||||||
if (isLoading && loadingProgress < 100) {
|
if (isLoading && loadingProgress < 100) {
|
||||||
@@ -128,7 +145,7 @@ const DlcSelectionDialog = ({
|
|||||||
preventBackdropClose={isLoading}
|
preventBackdropClose={isLoading}
|
||||||
>
|
>
|
||||||
<DialogHeader onClose={onClose}>
|
<DialogHeader onClose={onClose}>
|
||||||
<h3>{isEditMode ? 'Edit DLCs' : 'Select DLCs to Enable'}</h3>
|
<h3>{dialogTitle}</h3>
|
||||||
<div className="dlc-game-info">
|
<div className="dlc-game-info">
|
||||||
<span className="game-title">{gameTitle}</span>
|
<span className="game-title">{gameTitle}</span>
|
||||||
<span className="dlc-count">
|
<span className="dlc-count">
|
||||||
@@ -155,7 +172,7 @@ const DlcSelectionDialog = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isLoading && (
|
{isLoading && loadingProgress > 0 && (
|
||||||
<div className="dlc-loading-progress">
|
<div className="dlc-loading-progress">
|
||||||
<div className="progress-bar-container">
|
<div className="progress-bar-container">
|
||||||
<div className="progress-bar" style={{ width: `${loadingProgress}%` }} />
|
<div className="progress-bar" style={{ width: `${loadingProgress}%` }} />
|
||||||
@@ -210,7 +227,7 @@ const DlcSelectionDialog = ({
|
|||||||
onClick={handleConfirm}
|
onClick={handleConfirm}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
{isEditMode ? 'Save Changes' : 'Install with Selected DLCs'}
|
{actionButtonText}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export const AppProvider = ({ children }: AppProviderProps) => {
|
|||||||
setDlcDialog,
|
setDlcDialog,
|
||||||
handleDlcDialogClose: closeDlcDialog,
|
handleDlcDialogClose: closeDlcDialog,
|
||||||
streamGameDlcs,
|
streamGameDlcs,
|
||||||
|
handleGameEdit,
|
||||||
} = useDlcManager()
|
} = useDlcManager()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -47,35 +48,7 @@ export const AppProvider = ({ children }: AppProviderProps) => {
|
|||||||
info
|
info
|
||||||
} = useToasts()
|
} = useToasts()
|
||||||
|
|
||||||
// Combined handler for game edit
|
// Game action handler with proper error reporting
|
||||||
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 {
|
|
||||||
// Open the dialog
|
|
||||||
setDlcDialog({
|
|
||||||
...dlcDialog,
|
|
||||||
visible: true,
|
|
||||||
gameId,
|
|
||||||
gameTitle: game.title,
|
|
||||||
isLoading: true,
|
|
||||||
isEditMode: true,
|
|
||||||
dlcs: [], // start empty
|
|
||||||
progress: 0,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Now fetch DLCs in the background
|
|
||||||
streamGameDlcs(gameId)
|
|
||||||
} catch (error) {
|
|
||||||
showError(`Failed to load DLCs: ${error}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enhanced game action handler with proper error reporting
|
|
||||||
const handleGameAction = async (gameId: string, action: ActionType) => {
|
const handleGameAction = async (gameId: string, action: ActionType) => {
|
||||||
const game = games.find(g => g.id === gameId)
|
const game = games.find(g => g.id === gameId)
|
||||||
if (!game) {
|
if (!game) {
|
||||||
@@ -83,6 +56,31 @@ export const AppProvider = ({ children }: AppProviderProps) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For DLC installation, we want to show the DLC selection dialog first
|
||||||
|
if (action === 'install_cream') {
|
||||||
|
try {
|
||||||
|
// Show DLC selection dialog
|
||||||
|
setDlcDialog({
|
||||||
|
...dlcDialog,
|
||||||
|
visible: true,
|
||||||
|
gameId,
|
||||||
|
gameTitle: game.title,
|
||||||
|
dlcs: [], // Start with empty list
|
||||||
|
isLoading: true,
|
||||||
|
isEditMode: false, // This is a new installation
|
||||||
|
progress: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Start streaming DLCs
|
||||||
|
streamGameDlcs(gameId)
|
||||||
|
return
|
||||||
|
} catch (error) {
|
||||||
|
showError(`Failed to prepare DLC installation: ${error}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For other actions (uninstall cream, install/uninstall smoke)
|
||||||
// Mark game as installing
|
// Mark game as installing
|
||||||
setGames(prevGames =>
|
setGames(prevGames =>
|
||||||
prevGames.map(g => g.id === gameId ? {...g, installing: true} : g)
|
prevGames.map(g => g.id === gameId ? {...g, installing: true} : g)
|
||||||
@@ -154,7 +152,9 @@ export const AppProvider = ({ children }: AppProviderProps) => {
|
|||||||
|
|
||||||
// DLC management
|
// DLC management
|
||||||
dlcDialog,
|
dlcDialog,
|
||||||
handleGameEdit,
|
handleGameEdit: (gameId: string) => {
|
||||||
|
handleGameEdit(gameId, games)
|
||||||
|
},
|
||||||
handleDlcDialogClose: closeDlcDialog,
|
handleDlcDialogClose: closeDlcDialog,
|
||||||
|
|
||||||
// Game actions
|
// Game actions
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export function useDlcManager() {
|
|||||||
const unlistenDlcFound = await listen<string>('dlc-found', (event) => {
|
const unlistenDlcFound = await listen<string>('dlc-found', (event) => {
|
||||||
const dlc = JSON.parse(event.payload) as { appid: string; name: string }
|
const dlc = JSON.parse(event.payload) as { appid: string; name: string }
|
||||||
|
|
||||||
// Add the DLC to the current list with enabled=true
|
// Add the DLC to the current list with enabled=true by default
|
||||||
setDlcDialog((prev) => ({
|
setDlcDialog((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
dlcs: [...prev.dlcs, { ...dlc, enabled: true }],
|
dlcs: [...prev.dlcs, { ...dlc, enabled: true }],
|
||||||
@@ -175,7 +175,7 @@ export function useDlcManager() {
|
|||||||
dlcs: [],
|
dlcs: [],
|
||||||
enabledDlcs: [],
|
enabledDlcs: [],
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
isEditMode: true,
|
isEditMode: true, // This is an edit operation
|
||||||
progress: 0,
|
progress: 0,
|
||||||
progressMessage: 'Reading DLC configuration...',
|
progressMessage: 'Reading DLC configuration...',
|
||||||
timeLeft: '',
|
timeLeft: '',
|
||||||
|
|||||||
@@ -81,6 +81,13 @@ export function useGameActions() {
|
|||||||
// Unified handler for game actions (install/uninstall)
|
// Unified handler for game actions (install/uninstall)
|
||||||
const handleGameAction = useCallback(async (gameId: string, action: ActionType, games: Game[]) => {
|
const handleGameAction = useCallback(async (gameId: string, action: ActionType, games: Game[]) => {
|
||||||
try {
|
try {
|
||||||
|
// For CreamLinux installation, we should NOT call process_game_action directly
|
||||||
|
// Instead, we show the DLC selection dialog first, which is handled in AppProvider
|
||||||
|
if (action === 'install_cream') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// For other actions (uninstall_cream, install_smoke, uninstall_smoke)
|
||||||
// Find game to get title
|
// Find game to get title
|
||||||
const game = games.find((g) => g.id === gameId)
|
const game = games.find((g) => g.id === gameId)
|
||||||
if (!game) return
|
if (!game) return
|
||||||
@@ -179,7 +186,7 @@ export function useGameActions() {
|
|||||||
setProgressDialog({
|
setProgressDialog({
|
||||||
visible: true,
|
visible: true,
|
||||||
title: `Installing CreamLinux for ${game.title}`,
|
title: `Installing CreamLinux for ${game.title}`,
|
||||||
message: 'Processing...',
|
message: 'Preparing to download CreamLinux...',
|
||||||
progress: 0,
|
progress: 0,
|
||||||
showInstructions: false,
|
showInstructions: false,
|
||||||
instructions: undefined,
|
instructions: undefined,
|
||||||
@@ -190,6 +197,8 @@ export function useGameActions() {
|
|||||||
gameId,
|
gameId,
|
||||||
selectedDlcs,
|
selectedDlcs,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Note: The progress dialog will be updated through the installation-progress event listener
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error processing DLC selection:', error)
|
console.error('Error processing DLC selection:', error)
|
||||||
|
|||||||
Reference in New Issue
Block a user