feat: improved launch settings
This commit is contained in:
@ -18,6 +18,7 @@ import {
|
|||||||
initMinecraftHandlers,
|
initMinecraftHandlers,
|
||||||
initAuthHandlers,
|
initAuthHandlers,
|
||||||
initServerStatusHandler,
|
initServerStatusHandler,
|
||||||
|
initPackConfigHandlers,
|
||||||
} from './minecraft-launcher';
|
} from './minecraft-launcher';
|
||||||
|
|
||||||
class AppUpdater {
|
class AppUpdater {
|
||||||
@ -133,6 +134,7 @@ const createWindow = async () => {
|
|||||||
initAuthHandlers();
|
initAuthHandlers();
|
||||||
initMinecraftHandlers();
|
initMinecraftHandlers();
|
||||||
initServerStatusHandler();
|
initServerStatusHandler();
|
||||||
|
initPackConfigHandlers();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -477,7 +477,7 @@ export function initMinecraftHandlers() {
|
|||||||
accessToken,
|
accessToken,
|
||||||
uuid,
|
uuid,
|
||||||
username,
|
username,
|
||||||
memory = 2048,
|
memory = 4096,
|
||||||
baseVersion = '1.21.4',
|
baseVersion = '1.21.4',
|
||||||
fabricVersion = 'fabric0.16.14',
|
fabricVersion = 'fabric0.16.14',
|
||||||
packName = 'Comfort', // Название основной сборки
|
packName = 'Comfort', // Название основной сборки
|
||||||
@ -761,6 +761,47 @@ export function initMinecraftHandlers() {
|
|||||||
return { success: false, error: error.message };
|
return { success: false, error: error.message };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Добавьте в функцию initMinecraftHandlers или создайте новую
|
||||||
|
ipcMain.handle('get-pack-files', async (event, packName) => {
|
||||||
|
try {
|
||||||
|
const appPath = path.dirname(app.getPath('exe'));
|
||||||
|
const minecraftDir = path.join(appPath, '.minecraft');
|
||||||
|
const packDir = path.join(minecraftDir, 'versions', packName);
|
||||||
|
|
||||||
|
if (!fs.existsSync(packDir)) {
|
||||||
|
return { success: false, error: 'Директория сборки не найдена' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для рекурсивного обхода директории
|
||||||
|
const scanDir: any = (dir: any, basePath: any = '') => {
|
||||||
|
const result = [];
|
||||||
|
const items = fs.readdirSync(dir);
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
const itemPath = path.join(dir, item);
|
||||||
|
const relativePath = basePath ? path.join(basePath, item) : item;
|
||||||
|
const isDirectory = fs.statSync(itemPath).isDirectory();
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
name: item,
|
||||||
|
path: relativePath,
|
||||||
|
isDirectory,
|
||||||
|
// Если это директория, рекурсивно сканируем ее
|
||||||
|
children: isDirectory ? scanDir(itemPath, relativePath) : [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const files = scanDir(packDir);
|
||||||
|
return { success: true, files };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка при получении файлов сборки:', error);
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Добавляем обработчики IPC для аутентификации
|
// Добавляем обработчики IPC для аутентификации
|
||||||
@ -855,3 +896,84 @@ export function initServerStatusHandler() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Функция для работы с конфигурацией сборки
|
||||||
|
export function initPackConfigHandlers() {
|
||||||
|
// Файл конфигурации
|
||||||
|
const CONFIG_FILENAME = 'popa-launcher-config.json';
|
||||||
|
|
||||||
|
// Обработчик для сохранения настроек сборки
|
||||||
|
ipcMain.handle('save-pack-config', async (event, { packName, config }) => {
|
||||||
|
try {
|
||||||
|
const appPath = path.dirname(app.getPath('exe'));
|
||||||
|
const minecraftDir = path.join(appPath, '.minecraft');
|
||||||
|
const packDir = path.join(minecraftDir, 'versions', packName);
|
||||||
|
|
||||||
|
// Создаем папку для сборки, если она не существует
|
||||||
|
if (!fs.existsSync(packDir)) {
|
||||||
|
fs.mkdirSync(packDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const configPath = path.join(packDir, CONFIG_FILENAME);
|
||||||
|
|
||||||
|
// Сохраняем конфигурацию в файл
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
||||||
|
|
||||||
|
// Добавляем файл конфигурации в список файлов, которые не удаляются
|
||||||
|
if (!config.preserveFiles.includes(CONFIG_FILENAME)) {
|
||||||
|
config.preserveFiles.push(CONFIG_FILENAME);
|
||||||
|
// Перезаписываем файл с обновленным списком
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка при сохранении настроек сборки:', error);
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Обработчик для загрузки настроек сборки
|
||||||
|
ipcMain.handle('load-pack-config', async (event, { packName }) => {
|
||||||
|
try {
|
||||||
|
const appPath = path.dirname(app.getPath('exe'));
|
||||||
|
const minecraftDir = path.join(appPath, '.minecraft');
|
||||||
|
const packDir = path.join(minecraftDir, 'versions', packName);
|
||||||
|
const configPath = path.join(packDir, CONFIG_FILENAME);
|
||||||
|
|
||||||
|
// Проверяем существование файла конфигурации
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
// Если файла нет, возвращаем дефолтную конфигурацию
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
config: {
|
||||||
|
memory: 4096,
|
||||||
|
preserveFiles: [CONFIG_FILENAME], // По умолчанию сохраняем файл конфигурации
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Читаем и парсим конфигурацию
|
||||||
|
const configData = fs.readFileSync(configPath, 'utf8');
|
||||||
|
const config = JSON.parse(configData);
|
||||||
|
|
||||||
|
// Добавляем файл конфигурации в список сохраняемых файлов, если его там нет
|
||||||
|
if (!config.preserveFiles.includes(CONFIG_FILENAME)) {
|
||||||
|
config.preserveFiles.push(CONFIG_FILENAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true, config };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка при загрузке настроек сборки:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
// Возвращаем дефолтную конфигурацию при ошибке
|
||||||
|
config: {
|
||||||
|
memory: 4096,
|
||||||
|
preserveFiles: [CONFIG_FILENAME],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -7,7 +7,9 @@ export type Channels =
|
|||||||
| 'installation-status'
|
| 'installation-status'
|
||||||
| 'get-server-status'
|
| 'get-server-status'
|
||||||
| 'close-app'
|
| 'close-app'
|
||||||
| 'minimize-app';
|
| 'minimize-app'
|
||||||
|
| 'save-pack-config'
|
||||||
|
| 'load-pack-config';
|
||||||
|
|
||||||
const electronHandler = {
|
const electronHandler = {
|
||||||
ipcRenderer: {
|
ipcRenderer: {
|
||||||
|
@ -23,13 +23,6 @@ const launchOptions = {
|
|||||||
baseVersion: '1.21.4',
|
baseVersion: '1.21.4',
|
||||||
serverIp: 'popa-popa.ru',
|
serverIp: 'popa-popa.ru',
|
||||||
fabricVersion: 'fabric0.16.14',
|
fabricVersion: 'fabric0.16.14',
|
||||||
preserveFiles: [
|
|
||||||
'options.txt',
|
|
||||||
'screenshots',
|
|
||||||
'schematics',
|
|
||||||
'syncmatics',
|
|
||||||
'saves',
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const AuthCheck = ({ children }: { children: ReactNode }) => {
|
const AuthCheck = ({ children }: { children: ReactNode }) => {
|
||||||
|
205
src/renderer/components/FilesSelector.tsx
Normal file
205
src/renderer/components/FilesSelector.tsx
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Checkbox,
|
||||||
|
Typography,
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
ListItemIcon,
|
||||||
|
ListItemText,
|
||||||
|
Collapse,
|
||||||
|
CircularProgress,
|
||||||
|
} from '@mui/material';
|
||||||
|
import FolderIcon from '@mui/icons-material/Folder';
|
||||||
|
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
|
||||||
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
|
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||||
|
|
||||||
|
interface FileNode {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
isDirectory: boolean;
|
||||||
|
children: FileNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FilesSelectorProps {
|
||||||
|
packName: string;
|
||||||
|
onSelectionChange: (selectedFiles: string[]) => void;
|
||||||
|
initialSelected?: string[]; // Добавляем этот параметр
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FilesSelector({
|
||||||
|
packName,
|
||||||
|
onSelectionChange,
|
||||||
|
initialSelected = [], // Значение по умолчанию
|
||||||
|
}: FilesSelectorProps) {
|
||||||
|
const [files, setFiles] = useState<FileNode[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
// Используем initialSelected для начального состояния
|
||||||
|
const [selectedFiles, setSelectedFiles] = useState<string[]>(initialSelected);
|
||||||
|
const [expandedFolders, setExpandedFolders] = useState<Set<string>>(
|
||||||
|
new Set(),
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchFiles = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await window.electron.ipcRenderer.invoke(
|
||||||
|
'get-pack-files',
|
||||||
|
packName,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
setFiles(result.files);
|
||||||
|
} else {
|
||||||
|
setError(result.error);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setError('Ошибка при загрузке файлов');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchFiles();
|
||||||
|
}, [packName]);
|
||||||
|
|
||||||
|
// Обработка выбора файла/папки
|
||||||
|
const handleToggle = (
|
||||||
|
path: string,
|
||||||
|
isDirectory: boolean,
|
||||||
|
children: FileNode[],
|
||||||
|
) => {
|
||||||
|
let newSelected = [...selectedFiles];
|
||||||
|
|
||||||
|
if (isDirectory) {
|
||||||
|
if (selectedFiles.includes(path)) {
|
||||||
|
// Если папка выбрана, убираем ее и все вложенные файлы
|
||||||
|
newSelected = newSelected.filter((p) => !p.startsWith(path));
|
||||||
|
} else {
|
||||||
|
// Если папка не выбрана, добавляем ее и все вложенные файлы
|
||||||
|
newSelected.push(path);
|
||||||
|
const addChildPaths = (nodes: FileNode[]) => {
|
||||||
|
for (const node of nodes) {
|
||||||
|
newSelected.push(node.path);
|
||||||
|
if (node.isDirectory) {
|
||||||
|
addChildPaths(node.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
addChildPaths(children);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Для обычного файла просто переключаем состояние
|
||||||
|
if (selectedFiles.includes(path)) {
|
||||||
|
newSelected = newSelected.filter((p) => p !== path);
|
||||||
|
} else {
|
||||||
|
newSelected.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedFiles(newSelected);
|
||||||
|
onSelectionChange(newSelected);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Переключение раскрытия папки
|
||||||
|
const toggleFolder = (path: string) => {
|
||||||
|
const newExpanded = new Set(expandedFolders);
|
||||||
|
if (expandedFolders.has(path)) {
|
||||||
|
newExpanded.delete(path);
|
||||||
|
} else {
|
||||||
|
newExpanded.add(path);
|
||||||
|
}
|
||||||
|
setExpandedFolders(newExpanded);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Рекурсивный компонент для отображения файлов и папок
|
||||||
|
const renderFileTree = (nodes: FileNode[]) => {
|
||||||
|
// Сортировка: сначала папки, потом файлы
|
||||||
|
const sortedNodes = [...nodes].sort((a, b) => {
|
||||||
|
// Если у элементов разные типы (папка/файл)
|
||||||
|
if (a.isDirectory !== b.isDirectory) {
|
||||||
|
return a.isDirectory ? -1 : 1; // Папки идут первыми
|
||||||
|
}
|
||||||
|
// Если оба элемента одного типа, сортируем по алфавиту
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List dense>
|
||||||
|
{sortedNodes.map((node) => (
|
||||||
|
<div key={node.path}>
|
||||||
|
<ListItem
|
||||||
|
sx={{
|
||||||
|
borderRadius: '3vw',
|
||||||
|
backgroundColor: '#FFFFFF1A',
|
||||||
|
marginBottom: '1vh',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Checkbox
|
||||||
|
edge="start"
|
||||||
|
checked={selectedFiles.includes(node.path)}
|
||||||
|
onChange={() =>
|
||||||
|
handleToggle(node.path, node.isDirectory, node.children)
|
||||||
|
}
|
||||||
|
tabIndex={-1}
|
||||||
|
sx={{ color: 'white' }}
|
||||||
|
/>
|
||||||
|
</ListItemIcon>
|
||||||
|
|
||||||
|
{node.isDirectory && (
|
||||||
|
<ListItemIcon onClick={() => toggleFolder(node.path)}>
|
||||||
|
{expandedFolders.has(node.path) ? (
|
||||||
|
<ExpandLessIcon sx={{ color: 'white' }} />
|
||||||
|
) : (
|
||||||
|
<ExpandMoreIcon sx={{ color: 'white' }} />
|
||||||
|
)}
|
||||||
|
</ListItemIcon>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ListItemIcon>
|
||||||
|
{node.isDirectory ? (
|
||||||
|
<FolderIcon sx={{ color: 'white' }} />
|
||||||
|
) : (
|
||||||
|
<InsertDriveFileIcon sx={{ color: 'white' }} />
|
||||||
|
)}
|
||||||
|
</ListItemIcon>
|
||||||
|
|
||||||
|
<ListItemText
|
||||||
|
primary={node.name}
|
||||||
|
sx={{ color: 'white', fontFamily: 'Benzin-Bold' }}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
|
||||||
|
{node.isDirectory && node.children.length > 0 && (
|
||||||
|
<Collapse
|
||||||
|
in={expandedFolders.has(node.path)}
|
||||||
|
timeout="auto"
|
||||||
|
unmountOnExit
|
||||||
|
>
|
||||||
|
<Box sx={{ pl: 4 }}>{renderFileTree(node.children)}</Box>
|
||||||
|
</Collapse>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <CircularProgress />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <Typography color="error">{error}</Typography>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ maxHeight: '300px', overflow: 'auto' }}>
|
||||||
|
{renderFileTree(files)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
@ -33,7 +33,6 @@ const useConfig = () => {
|
|||||||
return {
|
return {
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
memory: 4096,
|
|
||||||
comfortVersion: '',
|
comfortVersion: '',
|
||||||
accessToken: '',
|
accessToken: '',
|
||||||
clientToken: '',
|
clientToken: '',
|
||||||
|
@ -5,11 +5,16 @@ import {
|
|||||||
Snackbar,
|
Snackbar,
|
||||||
Alert,
|
Alert,
|
||||||
LinearProgress,
|
LinearProgress,
|
||||||
|
Modal,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import ServerStatus from '../components/ServerStatus/ServerStatus';
|
import ServerStatus from '../components/ServerStatus/ServerStatus';
|
||||||
import PopaPopa from '../components/popa-popa';
|
import PopaPopa from '../components/popa-popa';
|
||||||
|
import SettingsIcon from '@mui/icons-material/Settings';
|
||||||
|
import React from 'react';
|
||||||
|
import MemorySlider from '../components/Login/MemorySlider';
|
||||||
|
import FilesSelector from '../components/FilesSelector';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@ -34,12 +39,19 @@ interface LaunchPageProps {
|
|||||||
baseVersion: string;
|
baseVersion: string;
|
||||||
serverIp: string;
|
serverIp: string;
|
||||||
fabricVersion: string;
|
fabricVersion: string;
|
||||||
preserveFiles: string[];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
// Начальное состояние должно быть пустым или с минимальными значениями
|
||||||
|
const [config, setConfig] = useState<{
|
||||||
|
memory: number;
|
||||||
|
preserveFiles: string[];
|
||||||
|
}>({
|
||||||
|
memory: 0,
|
||||||
|
preserveFiles: [],
|
||||||
|
});
|
||||||
const [isDownloading, setIsDownloading] = useState(false);
|
const [isDownloading, setIsDownloading] = useState(false);
|
||||||
const [downloadProgress, setDownloadProgress] = useState(0);
|
const [downloadProgress, setDownloadProgress] = useState(0);
|
||||||
const [buffer, setBuffer] = useState(10);
|
const [buffer, setBuffer] = useState(10);
|
||||||
@ -51,6 +63,9 @@ const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
|||||||
}>({ open: false, message: '', severity: 'info' });
|
}>({ open: false, message: '', severity: 'info' });
|
||||||
const [installStep, setInstallStep] = useState('');
|
const [installStep, setInstallStep] = useState('');
|
||||||
const [installMessage, setInstallMessage] = useState('');
|
const [installMessage, setInstallMessage] = useState('');
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
const handleOpen = () => setOpen(true);
|
||||||
|
const handleClose = () => setOpen(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const savedConfig = localStorage.getItem('launcher_config');
|
const savedConfig = localStorage.getItem('launcher_config');
|
||||||
@ -85,6 +100,29 @@ const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
|||||||
};
|
};
|
||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Загрузка конфигурации сборки при монтировании
|
||||||
|
const loadPackConfig = async () => {
|
||||||
|
try {
|
||||||
|
const result = await window.electron.ipcRenderer.invoke(
|
||||||
|
'load-pack-config',
|
||||||
|
{
|
||||||
|
packName: launchOptions.packName,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.success && result.config) {
|
||||||
|
// Полностью заменяем config значениями из файла
|
||||||
|
setConfig(result.config);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка при загрузке настроек:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadPackConfig();
|
||||||
|
}, [launchOptions.packName]);
|
||||||
|
|
||||||
const showNotification = (
|
const showNotification = (
|
||||||
message: string,
|
message: string,
|
||||||
severity: 'success' | 'error' | 'info',
|
severity: 'success' | 'error' | 'info',
|
||||||
@ -102,6 +140,19 @@ const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
|||||||
setDownloadProgress(0);
|
setDownloadProgress(0);
|
||||||
setBuffer(10);
|
setBuffer(10);
|
||||||
|
|
||||||
|
// Загружаем настройки сборки
|
||||||
|
const result = await window.electron.ipcRenderer.invoke(
|
||||||
|
'load-pack-config',
|
||||||
|
{
|
||||||
|
packName: launchOptions.packName,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Используйте уже существующий state вместо локальной переменной
|
||||||
|
if (result.success && result.config) {
|
||||||
|
setConfig(result.config); // Обновляем state
|
||||||
|
}
|
||||||
|
|
||||||
const savedConfig = JSON.parse(
|
const savedConfig = JSON.parse(
|
||||||
localStorage.getItem('launcher_config') || '{}',
|
localStorage.getItem('launcher_config') || '{}',
|
||||||
);
|
);
|
||||||
@ -112,7 +163,7 @@ const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
|||||||
apiReleaseUrl: launchOptions.apiReleaseUrl,
|
apiReleaseUrl: launchOptions.apiReleaseUrl,
|
||||||
versionFileName: launchOptions.versionFileName,
|
versionFileName: launchOptions.versionFileName,
|
||||||
packName: launchOptions.packName,
|
packName: launchOptions.packName,
|
||||||
preserveFiles: launchOptions.preserveFiles,
|
preserveFiles: config.preserveFiles,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Передаем опции для скачивания
|
// Передаем опции для скачивания
|
||||||
@ -142,7 +193,7 @@ const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
|||||||
accessToken: savedConfig.accessToken,
|
accessToken: savedConfig.accessToken,
|
||||||
uuid: savedConfig.uuid,
|
uuid: savedConfig.uuid,
|
||||||
username: savedConfig.username,
|
username: savedConfig.username,
|
||||||
memory: launchOptions.memory,
|
memory: config.memory, // Используем state
|
||||||
baseVersion: launchOptions.baseVersion,
|
baseVersion: launchOptions.baseVersion,
|
||||||
packName: launchOptions.packName,
|
packName: launchOptions.packName,
|
||||||
serverIp: launchOptions.serverIp,
|
serverIp: launchOptions.serverIp,
|
||||||
@ -180,6 +231,29 @@ const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Функция для сохранения настроек
|
||||||
|
const savePackConfig = async () => {
|
||||||
|
try {
|
||||||
|
const configToSave = {
|
||||||
|
memory: config.memory,
|
||||||
|
preserveFiles: config.preserveFiles || [],
|
||||||
|
};
|
||||||
|
|
||||||
|
await window.electron.ipcRenderer.invoke('save-pack-config', {
|
||||||
|
packName: launchOptions.packName,
|
||||||
|
config: configToSave,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Обновляем launchOptions
|
||||||
|
launchOptions.memory = config.memory;
|
||||||
|
|
||||||
|
showNotification('Настройки сохранены', 'success');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка при сохранении настроек:', error);
|
||||||
|
showNotification('Ошибка сохранения настроек', 'error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ gap: '1vh', display: 'flex', flexDirection: 'column' }}>
|
<Box sx={{ gap: '1vh', display: 'flex', flexDirection: 'column' }}>
|
||||||
<PopaPopa />
|
<PopaPopa />
|
||||||
@ -235,13 +309,46 @@ const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: '1vw',
|
||||||
|
width: '100%', // родитель занимает всю ширину
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Первая кнопка — растягивается на всё доступное пространство */}
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={handleLaunchMinecraft}
|
onClick={handleLaunchMinecraft}
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1, // занимает всё свободное место
|
||||||
|
width: 'auto', // ширина подстраивается
|
||||||
|
borderRadius: '3vw',
|
||||||
|
fontFamily: 'Benzin-Bold',
|
||||||
|
background: 'linear-gradient(90deg, #3B96FF 0%, #FFB7ED 100%)',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Запустить Minecraft
|
Запустить Minecraft
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
{/* Вторая кнопка — квадратная, фиксированного размера (ширина = высоте) */}
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
sx={{
|
||||||
|
flexShrink: 0, // не сжимается
|
||||||
|
aspectRatio: '1', // ширина = высоте
|
||||||
|
backgroundColor: 'grey',
|
||||||
|
borderRadius: '3vw',
|
||||||
|
minHeight: 'unset',
|
||||||
|
minWidth: 'unset',
|
||||||
|
height: '100%', // занимает полную высоту родителя
|
||||||
|
}}
|
||||||
|
onClick={handleOpen}
|
||||||
|
>
|
||||||
|
<SettingsIcon />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Snackbar
|
<Snackbar
|
||||||
@ -257,6 +364,66 @@ const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
|||||||
{notification.message}
|
{notification.message}
|
||||||
</Alert>
|
</Alert>
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
aria-labelledby="modal-modal-title"
|
||||||
|
aria-describedby="modal-modal-description"
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
width: 400,
|
||||||
|
background:
|
||||||
|
'linear-gradient(-242.94deg, #000000 39.07%, #3b4187 184.73%)',
|
||||||
|
border: '2px solid #000',
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 4,
|
||||||
|
borderRadius: '3vw',
|
||||||
|
gap: '1vh',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography id="modal-modal-title" variant="body1" component="h2">
|
||||||
|
Файлы и папки, которые будут сохранены после переустановки сборки
|
||||||
|
</Typography>
|
||||||
|
<FilesSelector
|
||||||
|
packName={launchOptions.packName}
|
||||||
|
initialSelected={config.preserveFiles} // Передаем текущие выбранные файлы
|
||||||
|
onSelectionChange={(selected) => {
|
||||||
|
setConfig((prev) => ({ ...prev, preserveFiles: selected }));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography variant="body1" sx={{ color: 'white' }}>
|
||||||
|
Оперативная память выделенная для Minecraft
|
||||||
|
</Typography>
|
||||||
|
<MemorySlider
|
||||||
|
memory={config.memory}
|
||||||
|
onChange={(e, value) => {
|
||||||
|
setConfig((prev) => ({ ...prev, memory: value as number }));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="success"
|
||||||
|
onClick={() => {
|
||||||
|
savePackConfig();
|
||||||
|
handleClose();
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
borderRadius: '3vw',
|
||||||
|
fontFamily: 'Benzin-Bold',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Сохранить
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { Box, Typography } from '@mui/material';
|
import { Box, Typography } from '@mui/material';
|
||||||
import useConfig from '../hooks/useConfig';
|
|
||||||
import useAuth from '../hooks/useAuth';
|
import useAuth from '../hooks/useAuth';
|
||||||
import AuthForm from '../components/Login/AuthForm';
|
import AuthForm from '../components/Login/AuthForm';
|
||||||
import MemorySlider from '../components/Login/MemorySlider';
|
import MemorySlider from '../components/Login/MemorySlider';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import PopaPopa from '../components/popa-popa';
|
import PopaPopa from '../components/popa-popa';
|
||||||
|
import useConfig from '../hooks/useConfig';
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
Reference in New Issue
Block a user