mirror of
https://github.com/Novattz/creamlinux-installer.git
synced 2026-01-31 07:42:52 -05:00
Compare commits
5 Commits
a00cc92b70
...
v1.3.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fd3147f44 | ||
|
|
87dc328434 | ||
|
|
b227dff339 | ||
|
|
04910e84cf | ||
|
|
7960019cd9 |
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,3 +1,13 @@
|
||||
## [1.3.2] - 23-12-2025
|
||||
|
||||
### Added
|
||||
- New dropdown component
|
||||
- Settings dialog for SmokeAPI configuration
|
||||
- Update creamlinux config functionality
|
||||
|
||||
### Changed
|
||||
- Adjusted styling for CreamLinux settings dialog
|
||||
|
||||
## [1.3.0] - 22-12-2025
|
||||
|
||||
### Added
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "creamlinux",
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "creamlinux",
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.5.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "creamlinux",
|
||||
"private": true,
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.2",
|
||||
"type": "module",
|
||||
"author": "Tickbase",
|
||||
"repository": "https://github.com/Novattz/creamlinux-installer",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "creamlinux-installer"
|
||||
version = "1.3.0"
|
||||
version = "1.3.2"
|
||||
description = "DLC Manager for Steam games on Linux"
|
||||
authors = ["tickbase"]
|
||||
license = "MIT"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"productName": "Creamlinux",
|
||||
"mainBinaryName": "creamlinux",
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.2",
|
||||
"identifier": "com.creamlinux.dev",
|
||||
"app": {
|
||||
"withGlobalTauri": false,
|
||||
|
||||
@@ -40,6 +40,7 @@ function App() {
|
||||
handleGameAction,
|
||||
handleDlcConfirm,
|
||||
handleGameEdit,
|
||||
handleUpdateDlcs,
|
||||
settingsDialog,
|
||||
handleSettingsOpen,
|
||||
handleSettingsClose,
|
||||
@@ -107,13 +108,18 @@ function App() {
|
||||
<DlcSelectionDialog
|
||||
visible={dlcDialog.visible}
|
||||
gameTitle={dlcDialog.gameTitle}
|
||||
gameId={dlcDialog.gameId}
|
||||
dlcs={dlcDialog.dlcs}
|
||||
isLoading={dlcDialog.isLoading}
|
||||
isEditMode={dlcDialog.isEditMode}
|
||||
isUpdating={dlcDialog.isUpdating}
|
||||
updateAttempted={dlcDialog.updateAttempted}
|
||||
loadingProgress={dlcDialog.progress}
|
||||
estimatedTimeLeft={dlcDialog.timeLeft}
|
||||
newDlcsCount={dlcDialog.newDlcsCount}
|
||||
onClose={handleDlcDialogClose}
|
||||
onConfirm={handleDlcConfirm}
|
||||
onUpdate={handleUpdateDlcs}
|
||||
/>
|
||||
|
||||
{/* Settings Dialog */}
|
||||
|
||||
@@ -6,17 +6,23 @@ import DialogFooter from './DialogFooter'
|
||||
import DialogActions from './DialogActions'
|
||||
import { Button, AnimatedCheckbox } from '@/components/buttons'
|
||||
import { DlcInfo } from '@/types'
|
||||
import { Icon, check, info } from '@/components/icons'
|
||||
|
||||
export interface DlcSelectionDialogProps {
|
||||
visible: boolean
|
||||
gameTitle: string
|
||||
gameId: string
|
||||
dlcs: DlcInfo[]
|
||||
onClose: () => void
|
||||
onConfirm: (selectedDlcs: DlcInfo[]) => void
|
||||
onUpdate?: (gameId: string) => void
|
||||
isLoading: boolean
|
||||
isEditMode?: boolean
|
||||
isUpdating?: boolean
|
||||
updateAttempted?: boolean
|
||||
loadingProgress?: number
|
||||
estimatedTimeLeft?: string
|
||||
newDlcsCount?: number
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,13 +33,18 @@ export interface DlcSelectionDialogProps {
|
||||
const DlcSelectionDialog = ({
|
||||
visible,
|
||||
gameTitle,
|
||||
gameId,
|
||||
dlcs,
|
||||
onClose,
|
||||
onConfirm,
|
||||
onUpdate,
|
||||
isLoading,
|
||||
isEditMode = false,
|
||||
isUpdating = false,
|
||||
updateAttempted = false,
|
||||
loadingProgress = 0,
|
||||
estimatedTimeLeft = '',
|
||||
newDlcsCount = 0,
|
||||
}: DlcSelectionDialogProps) => {
|
||||
// State for DLC management
|
||||
const [selectedDlcs, setSelectedDlcs] = useState<DlcInfo[]>([])
|
||||
@@ -169,13 +180,13 @@ const DlcSelectionDialog = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isLoading && loadingProgress > 0 && (
|
||||
{(isLoading || isUpdating) && loadingProgress > 0 && (
|
||||
<div className="dlc-loading-progress">
|
||||
<div className="progress-bar-container">
|
||||
<div className="progress-bar" style={{ width: `${loadingProgress}%` }} />
|
||||
</div>
|
||||
<div className="loading-details">
|
||||
<span>Loading DLCs: {loadingProgress}%</span>
|
||||
<span>{isUpdating ? 'Updating DLC list' : 'Loading DLCs'}: {loadingProgress}%</span>
|
||||
{estimatedTimeLeft && (
|
||||
<span className="time-left">Est. time left: {estimatedTimeLeft}</span>
|
||||
)}
|
||||
@@ -211,15 +222,47 @@ const DlcSelectionDialog = ({
|
||||
</DialogBody>
|
||||
|
||||
<DialogFooter>
|
||||
{/* Show update results */}
|
||||
{!isUpdating && !isLoading && isEditMode && updateAttempted && (
|
||||
<>
|
||||
{newDlcsCount > 0 && (
|
||||
<div className="dlc-update-results dlc-update-success">
|
||||
<span className="update-message">
|
||||
<Icon name={check} size="md" variant="solid" className="dlc-update-icon-success"/> Found {newDlcsCount} new DLC{newDlcsCount > 1 ? 's' : ''}!
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{newDlcsCount === 0 && (
|
||||
<div className="dlc-update-results dlc-update-info">
|
||||
<span className="update-message">
|
||||
<Icon name={info} size="md" variant="solid" className="dlc-update-icon-info"/> No new DLCs found. Your list is up to date!
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<DialogActions>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={onClose}
|
||||
disabled={isLoading && loadingProgress < 10}
|
||||
disabled={(isLoading || isUpdating) && loadingProgress < 10}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="primary" onClick={handleConfirm} disabled={isLoading}>
|
||||
|
||||
{/* Update button - only show in edit mode */}
|
||||
{isEditMode && onUpdate && (
|
||||
<Button
|
||||
variant="warning"
|
||||
onClick={() => onUpdate(gameId)}
|
||||
disabled={isLoading || isUpdating}
|
||||
>
|
||||
{isUpdating ? 'Updating...' : 'Update DLC List'}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button variant="primary" onClick={handleConfirm} disabled={isLoading || isUpdating}>
|
||||
{actionButtonText}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createContext } from 'react'
|
||||
import { Game, DlcInfo } from '@/types'
|
||||
import { ActionType } from '@/components/buttons/ActionButton'
|
||||
import { DlcDialogState } from '@/hooks/useDlcManager'
|
||||
|
||||
// Types for context sub-components
|
||||
export interface InstallationInstructions {
|
||||
@@ -10,17 +11,6 @@ export interface InstallationInstructions {
|
||||
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
|
||||
@@ -49,6 +39,7 @@ export interface AppContextType {
|
||||
dlcDialog: DlcDialogState
|
||||
handleGameEdit: (gameId: string) => void
|
||||
handleDlcDialogClose: () => void
|
||||
handleUpdateDlcs: (gameId: string) => Promise<void>
|
||||
|
||||
// Game actions
|
||||
progressDialog: ProgressDialogState
|
||||
|
||||
@@ -25,6 +25,7 @@ export const AppProvider = ({ children }: AppProviderProps) => {
|
||||
handleDlcDialogClose: closeDlcDialog,
|
||||
streamGameDlcs,
|
||||
handleGameEdit,
|
||||
handleUpdateDlcs,
|
||||
} = useDlcManager()
|
||||
|
||||
const {
|
||||
@@ -220,6 +221,7 @@ export const AppProvider = ({ children }: AppProviderProps) => {
|
||||
handleGameEdit(gameId, games)
|
||||
},
|
||||
handleDlcDialogClose: closeDlcDialog,
|
||||
handleUpdateDlcs: (gameId: string) => handleUpdateDlcs(gameId),
|
||||
|
||||
// Game actions
|
||||
progressDialog,
|
||||
|
||||
@@ -11,10 +11,13 @@ export interface DlcDialogState {
|
||||
enabledDlcs: string[]
|
||||
isLoading: boolean
|
||||
isEditMode: boolean
|
||||
isUpdating: boolean
|
||||
updateAttempted: boolean
|
||||
progress: number
|
||||
progressMessage: string
|
||||
timeLeft: string
|
||||
error: string | null
|
||||
newDlcsCount: number
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,10 +39,13 @@ export function useDlcManager() {
|
||||
enabledDlcs: [],
|
||||
isLoading: false,
|
||||
isEditMode: false,
|
||||
isUpdating: false,
|
||||
updateAttempted: false,
|
||||
progress: 0,
|
||||
progressMessage: '',
|
||||
timeLeft: '',
|
||||
error: null,
|
||||
newDlcsCount: 0,
|
||||
})
|
||||
|
||||
// Set up event listeners for DLC streaming
|
||||
@@ -80,6 +86,7 @@ export function useDlcManager() {
|
||||
setDlcDialog((prev) => ({
|
||||
...prev,
|
||||
isLoading: false,
|
||||
isUpdating: false,
|
||||
}))
|
||||
|
||||
// Reset fetch state
|
||||
@@ -177,10 +184,13 @@ export function useDlcManager() {
|
||||
enabledDlcs: [],
|
||||
isLoading: true,
|
||||
isEditMode: true,
|
||||
isUpdating: false,
|
||||
updateAttempted: false,
|
||||
progress: 0,
|
||||
progressMessage: 'Reading DLC configuration...',
|
||||
timeLeft: '',
|
||||
error: null,
|
||||
newDlcsCount: 0,
|
||||
})
|
||||
|
||||
// Always get a fresh copy from the config file
|
||||
@@ -302,6 +312,58 @@ export function useDlcManager() {
|
||||
}
|
||||
}, [dlcDialog.dlcs, dlcDialog.enabledDlcs])
|
||||
|
||||
// Function to update DLC list (refetch from Steam API)
|
||||
const handleUpdateDlcs = async (gameId: string) => {
|
||||
try {
|
||||
// Store current app IDs to identify new DLCs later
|
||||
const currentAppIds = new Set(dlcDialog.dlcs.map((dlc) => dlc.appid))
|
||||
|
||||
// Set updating state and clear DLCs
|
||||
setDlcDialog((prev) => ({
|
||||
...prev,
|
||||
isUpdating: true,
|
||||
isLoading: true,
|
||||
updateAttempted: true,
|
||||
progress: 0,
|
||||
progressMessage: 'Checking for new DLCs...',
|
||||
newDlcsCount: 0,
|
||||
dlcs: [], // Clear current DLCs to start fresh
|
||||
}))
|
||||
|
||||
// Mark that we're fetching DLCs for this game
|
||||
setIsFetchingDlcs(true)
|
||||
activeDlcFetchId.current = gameId
|
||||
|
||||
// Start streaming DLCs
|
||||
await streamGameDlcs(gameId)
|
||||
|
||||
// After streaming completes, calculate new DLCs
|
||||
// Wait a bit longer to ensure all DLCs have been added
|
||||
setTimeout(() => {
|
||||
setDlcDialog((prev) => {
|
||||
// Count how many DLCs are new (not in the original list)
|
||||
const actualNewCount = prev.dlcs.filter(dlc => !currentAppIds.has(dlc.appid)).length
|
||||
|
||||
console.log(`Update complete: Found ${actualNewCount} new DLCs out of ${prev.dlcs.length} total`)
|
||||
|
||||
return {
|
||||
...prev,
|
||||
newDlcsCount: actualNewCount,
|
||||
}
|
||||
})
|
||||
}, 1500) // Increased timeout to ensure all DLCs are processed
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error updating DLCs:', error)
|
||||
setDlcDialog((prev) => ({
|
||||
...prev,
|
||||
error: `Failed to update DLCs: ${error}`,
|
||||
isLoading: false,
|
||||
isUpdating: false,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
dlcDialog,
|
||||
setDlcDialog,
|
||||
@@ -309,6 +371,7 @@ export function useDlcManager() {
|
||||
streamGameDlcs,
|
||||
handleGameEdit,
|
||||
handleDlcDialogClose,
|
||||
handleUpdateDlcs,
|
||||
forceReload,
|
||||
}
|
||||
}
|
||||
@@ -154,6 +154,40 @@
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
// Update results message
|
||||
.dlc-update-results {
|
||||
padding: 0.75rem 1.5rem;
|
||||
background-color: var(--elevated-bg);
|
||||
border: 1px solid var(--border-soft);
|
||||
border-radius: var(--radius-sm);
|
||||
margin-bottom: 0.75rem;
|
||||
|
||||
.update-message {
|
||||
color: var(--text-primary);
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
&.dlc-update-success {
|
||||
.update-message {
|
||||
.dlc-update-icon-success {
|
||||
color: var(--success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.dlc-update-info {
|
||||
.update-message {
|
||||
.dlc-update-icon-info {
|
||||
color: var(--info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Game information in DLC dialog
|
||||
.dlc-game-info {
|
||||
display: flex;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"ignoreDeprecations": "6.0",
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
|
||||
Reference in New Issue
Block a user