mirror of
https://git.sanhost.net/sanasol/hytale-f2p
synced 2026-02-26 14:01:48 -03:00
Added mods version selector
Added mods version selector
This commit is contained in:
@@ -805,6 +805,25 @@
|
|||||||
<script src="js/featured.js"></script>
|
<script src="js/featured.js"></script>
|
||||||
<script type="module" src="js/settings.js"></script>
|
<script type="module" src="js/settings.js"></script>
|
||||||
<script type="module" src="js/update.js"></script>
|
<script type="module" src="js/update.js"></script>
|
||||||
|
|
||||||
|
<!-- Version Selection Modal (Isolated Container) -->
|
||||||
|
<div id="versionSelectModal" class="modal-overlay" style="display: none; position: fixed; inset: 0; z-index: 9999; background: rgba(0, 0, 0, 0.7); backdrop-filter: blur(5px); align-items: center; justify-content: center;">
|
||||||
|
<div class="glass-panel" style="width: 100%; max-width: 600px; max-height: 80vh; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; margin: 20px;">
|
||||||
|
<div class="modal-header" style="display: flex; justify-content: space-between; align-items: center; padding: 1.5rem; border-bottom: 1px solid rgba(255, 255, 255, 0.1);">
|
||||||
|
<h3 style="margin: 0; font-size: 1.25rem;">Select Version</h3>
|
||||||
|
<button id="closeVersionModal" class="modal-close" style="background: none; border: none; color: #a0a0a0; font-size: 1.25rem; cursor: pointer;"><i class="fas fa-times"></i></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" style="padding: 1.5rem; overflow-y: auto;">
|
||||||
|
<div id="versionList" class="version-list-container">
|
||||||
|
<div class="loading-versions" style="display: flex; flex-direction: column; align-items: center; gap: 1rem; color: #a0a0a0;">
|
||||||
|
<i class="fas fa-spinner fa-spin fa-2x"></i>
|
||||||
|
<span>Loading versions...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- updater.js disabled - using update.js instead which has skip button and macOS handling -->
|
<!-- updater.js disabled - using update.js instead which has skip button and macOS handling -->
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
134
GUI/js/mods.js
134
GUI/js/mods.js
@@ -78,6 +78,20 @@ function setupModsEventListeners() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const browseContainer = document.getElementById('browseModsList');
|
||||||
|
if (browseContainer) {
|
||||||
|
browseContainer.addEventListener('click', (e) => {
|
||||||
|
const installBtn = e.target.closest('[data-install-mod-id]');
|
||||||
|
if (installBtn) {
|
||||||
|
const modId = installBtn.getAttribute('data-install-mod-id');
|
||||||
|
const mod = browseMods.find(m => m.id == modId);
|
||||||
|
if (mod) {
|
||||||
|
openVersionSelectModal(mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openMyModsModal() {
|
function openMyModsModal() {
|
||||||
@@ -295,13 +309,6 @@ function displayBrowseMods(mods) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
browseContainer.innerHTML = mods.map(mod => createBrowseModCard(mod)).join('');
|
browseContainer.innerHTML = mods.map(mod => createBrowseModCard(mod)).join('');
|
||||||
|
|
||||||
mods.forEach(mod => {
|
|
||||||
const installBtn = document.getElementById(`install-${mod.id}`);
|
|
||||||
if (installBtn) {
|
|
||||||
installBtn.addEventListener('click', () => downloadAndInstallMod(mod));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createBrowseModCard(mod) {
|
function createBrowseModCard(mod) {
|
||||||
@@ -350,12 +357,12 @@ function createBrowseModCard(mod) {
|
|||||||
${window.i18n ? window.i18n.t('mods.view') : 'VIEW'}
|
${window.i18n ? window.i18n.t('mods.view') : 'VIEW'}
|
||||||
</button>
|
</button>
|
||||||
${!isInstalled ?
|
${!isInstalled ?
|
||||||
`<button id="install-${mod.id}" class="mod-btn-toggle bg-primary text-black hover:bg-primary/80">
|
`<button data-install-mod-id=\"${mod.id}\" class=\"mod-btn-toggle bg-primary text-black hover:bg-primary/80\">
|
||||||
<i class="fas fa-download"></i>
|
<i class=\"fas fa-download\"></i>
|
||||||
${window.i18n ? window.i18n.t('mods.install') : 'INSTALL'}
|
${window.i18n ? window.i18n.t('mods.install') : 'INSTALL'}
|
||||||
</button>` :
|
</button>` :
|
||||||
`<button class="mod-btn-toggle bg-white/10 text-white" disabled>
|
`<button class=\"mod-btn-toggle bg-white/10 text-white\" disabled>
|
||||||
<i class="fas fa-check"></i>
|
<i class=\"fas fa-check\"></i>
|
||||||
${window.i18n ? window.i18n.t('mods.installed') : 'INSTALLED'}
|
${window.i18n ? window.i18n.t('mods.installed') : 'INSTALLED'}
|
||||||
</button>`
|
</button>`
|
||||||
}
|
}
|
||||||
@@ -364,6 +371,104 @@ function createBrowseModCard(mod) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let currentSelectedMod = null;
|
||||||
|
|
||||||
|
function openVersionSelectModal(mod) {
|
||||||
|
currentSelectedMod = mod;
|
||||||
|
const modal = document.getElementById('versionSelectModal');
|
||||||
|
const closeBtn = document.getElementById('closeVersionModal');
|
||||||
|
const versionList = document.getElementById('versionList');
|
||||||
|
|
||||||
|
if (modal) {
|
||||||
|
modal.style.display = 'flex';
|
||||||
|
modal.classList.add('active');
|
||||||
|
|
||||||
|
const closeHandler = () => {
|
||||||
|
modal.classList.remove('active');
|
||||||
|
setTimeout(() => {
|
||||||
|
modal.style.display = 'none';
|
||||||
|
}, 300);
|
||||||
|
currentSelectedMod = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (closeBtn) {
|
||||||
|
closeBtn.onclick = closeHandler;
|
||||||
|
}
|
||||||
|
modal.onclick = (e) => {
|
||||||
|
if (e.target === modal) closeHandler();
|
||||||
|
};
|
||||||
|
|
||||||
|
loadModVersions(mod.id, versionList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadModVersions(modId, container) {
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="loading-versions">
|
||||||
|
<i class="fas fa-spinner fa-spin fa-2x" style="margin-bottom: 10px; display: block;"></i>
|
||||||
|
<span>Loading versions...</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const versions = await window.electronAPI.getModFiles(modId);
|
||||||
|
|
||||||
|
if (!versions || versions.length === 0) {
|
||||||
|
container.innerHTML = `<div class="p-4 text-center text-gray-400" style="padding: 2rem;">No versions found for this mod.</div>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort versions by date desc (API returns desc but ensure)
|
||||||
|
versions.sort((a, b) => new Date(b.fileDate) - new Date(a.fileDate));
|
||||||
|
|
||||||
|
container.innerHTML = versions.map(file => `
|
||||||
|
<div class="version-item">
|
||||||
|
<div class="version-info">
|
||||||
|
<div class="version-name">${file.displayName}</div>
|
||||||
|
<div class="version-meta">
|
||||||
|
<span><i class="fas fa-calendar"></i> ${new Date(file.fileDate).toLocaleDateString()}</span>
|
||||||
|
<span><i class="fas fa-download"></i> ${formatNumber(file.downloadCount)}</span>
|
||||||
|
<span><i class="fas fa-file-archive"></i> ${(file.fileLength / 1024 / 1024).toFixed(2)} MB</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="version-actions">
|
||||||
|
<button class="btn-install" data-file-id="${file.id}">
|
||||||
|
Install
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
|
||||||
|
// Add event listeners securely
|
||||||
|
container.querySelectorAll('.btn-install').forEach((btn, index) => {
|
||||||
|
const file = versions[index]; // Map index to file data
|
||||||
|
btn.onclick = () => installVersion(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading versions:', error);
|
||||||
|
container.innerHTML = `<div class="p-4 text-center text-red-400" style="padding: 2rem;">Error loading versions.<br><small>${error.message}</small></div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function installVersion(file) {
|
||||||
|
if (!currentSelectedMod) return;
|
||||||
|
|
||||||
|
const modal = document.getElementById('versionSelectModal');
|
||||||
|
modal.style.display = 'none';
|
||||||
|
|
||||||
|
const modInfo = {
|
||||||
|
...currentSelectedMod,
|
||||||
|
fileId: file.id,
|
||||||
|
downloadUrl: file.downloadUrl,
|
||||||
|
fileName: file.fileName,
|
||||||
|
fileSize: file.fileLength
|
||||||
|
};
|
||||||
|
|
||||||
|
await downloadAndInstallMod(modInfo);
|
||||||
|
currentSelectedMod = null;
|
||||||
|
}
|
||||||
|
|
||||||
async function downloadAndInstallMod(modInfo) {
|
async function downloadAndInstallMod(modInfo) {
|
||||||
try {
|
try {
|
||||||
const downloadMsg = window.i18n ? window.i18n.t('notifications.modsDownloading').replace('{name}', modInfo.name) : `Downloading ${modInfo.name}...`;
|
const downloadMsg = window.i18n ? window.i18n.t('notifications.modsDownloading').replace('{name}', modInfo.name) : `Downloading ${modInfo.name}...`;
|
||||||
@@ -762,7 +867,10 @@ window.modsManager = {
|
|||||||
closeMyModsModal,
|
closeMyModsModal,
|
||||||
viewModPage,
|
viewModPage,
|
||||||
loadInstalledMods,
|
loadInstalledMods,
|
||||||
loadBrowseMods
|
loadBrowseMods,
|
||||||
|
openVersionSelectModal
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', initModsManager);
|
// Remove auto-init since we are now calling it from script.js explicitly
|
||||||
|
// which guarantees order and environment readiness
|
||||||
|
// document.addEventListener('DOMContentLoaded', initModsManager);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import './ui.js';
|
|||||||
import './install.js';
|
import './install.js';
|
||||||
import './launcher.js';
|
import './launcher.js';
|
||||||
import './news.js';
|
import './news.js';
|
||||||
import './mods.js';
|
import { initModsManager } from './mods.js';
|
||||||
import './players.js';
|
import './players.js';
|
||||||
import './settings.js';
|
import './settings.js';
|
||||||
import './logs.js';
|
import './logs.js';
|
||||||
@@ -15,6 +15,12 @@ let i18nInitialized = false;
|
|||||||
|
|
||||||
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
||||||
updateLanguageSelector();
|
updateLanguageSelector();
|
||||||
|
initModsManager();
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
updateLanguageSelector();
|
||||||
|
initModsManager();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|||||||
@@ -6476,3 +6476,85 @@ input[type="text"].uuid-input,
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Version Selection Styles */
|
||||||
|
.version-list-container::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-list-container::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1rem;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-item:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
transform: translateX(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-name {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-meta {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #a0a0a0;
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-meta span {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-meta i {
|
||||||
|
font-size: 0.8em;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-actions .btn-install {
|
||||||
|
padding: 0.5rem 1.25rem;
|
||||||
|
background: linear-gradient(135deg, #3b82f6, #2563eb);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
transition: all 0.2s;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-actions .btn-install:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.3);
|
||||||
|
background: linear-gradient(135deg, #4f93f6, #3b82f6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-versions {
|
||||||
|
padding: 3rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
@@ -78,7 +78,8 @@ const {
|
|||||||
loadInstalledMods,
|
loadInstalledMods,
|
||||||
downloadMod,
|
downloadMod,
|
||||||
uninstallMod,
|
uninstallMod,
|
||||||
toggleMod
|
toggleMod,
|
||||||
|
getModFiles
|
||||||
} = require('./managers/modManager');
|
} = require('./managers/modManager');
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
@@ -181,6 +182,7 @@ module.exports = {
|
|||||||
downloadMod,
|
downloadMod,
|
||||||
uninstallMod,
|
uninstallMod,
|
||||||
toggleMod,
|
toggleMod,
|
||||||
|
getModFiles,
|
||||||
saveModsToConfig,
|
saveModsToConfig,
|
||||||
loadModsFromConfig,
|
loadModsFromConfig,
|
||||||
|
|
||||||
|
|||||||
@@ -285,6 +285,27 @@ async function toggleMod(modId, modsPath) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function getModFiles(modId) {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`https://api.curseforge.com/v1/mods/${modId}/files`, {
|
||||||
|
headers: {
|
||||||
|
'x-api-key': API_KEY,
|
||||||
|
'Accept': 'application/json'
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
pageSize: 20,
|
||||||
|
sortOrder: 'desc'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching mod files:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function syncModsForCurrentProfile() {
|
async function syncModsForCurrentProfile() {
|
||||||
try {
|
try {
|
||||||
const activeProfile = profileManager.getActiveProfile();
|
const activeProfile = profileManager.getActiveProfile();
|
||||||
@@ -455,5 +476,6 @@ module.exports = {
|
|||||||
syncModsForCurrentProfile,
|
syncModsForCurrentProfile,
|
||||||
generateModId,
|
generateModId,
|
||||||
extractModName,
|
extractModName,
|
||||||
extractVersion
|
extractVersion,
|
||||||
|
getModFiles
|
||||||
};
|
};
|
||||||
@@ -45,6 +45,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
loadInstalledMods: (modsPath) => ipcRenderer.invoke('load-installed-mods', modsPath),
|
loadInstalledMods: (modsPath) => ipcRenderer.invoke('load-installed-mods', modsPath),
|
||||||
downloadMod: (modInfo) => ipcRenderer.invoke('download-mod', modInfo),
|
downloadMod: (modInfo) => ipcRenderer.invoke('download-mod', modInfo),
|
||||||
uninstallMod: (modId, modsPath) => ipcRenderer.invoke('uninstall-mod', modId, modsPath),
|
uninstallMod: (modId, modsPath) => ipcRenderer.invoke('uninstall-mod', modId, modsPath),
|
||||||
|
getModFiles: (modId) => ipcRenderer.invoke('get-mod-files', modId),
|
||||||
toggleMod: (modId, modsPath) => ipcRenderer.invoke('toggle-mod', modId, modsPath),
|
toggleMod: (modId, modsPath) => ipcRenderer.invoke('toggle-mod', modId, modsPath),
|
||||||
selectModFiles: () => ipcRenderer.invoke('select-mod-files'),
|
selectModFiles: () => ipcRenderer.invoke('select-mod-files'),
|
||||||
copyModFile: (sourcePath, modsPath) => ipcRenderer.invoke('copy-mod-file', sourcePath, modsPath),
|
copyModFile: (sourcePath, modsPath) => ipcRenderer.invoke('copy-mod-file', sourcePath, modsPath),
|
||||||
|
|||||||
Reference in New Issue
Block a user