Compare commits

...

10 Commits

19 changed files with 276 additions and 310 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "lncvrt-games-launcher",
"private": true,
"version": "1.6.0",
"version": "1.6.1",
"type": "module",
"scripts": {
"dev": "next dev",

View File

@@ -1,6 +1,6 @@
[package]
name = "lncvrt-games-launcher"
version = "1.6.0"
version = "1.6.1"
authors = ["Lncvrt"]
edition = "2024"
@@ -22,13 +22,14 @@ tokio = "1.49.0"
futures-util = { version = "0.3.31", features = ["io"] }
tauri-plugin-decorum = "1.1.1"
tauri-plugin-fs = "2.4.5"
zip = "7.4.0"
zip = "8.0.0"
libc = "0.2.182"
tauri-plugin-dialog = "2.6.0"
tauri-plugin-notification = "2.3.3"
sysinfo = "0.38.1"
sha2 = "0.10.9"
tauri-plugin-clipboard-manager = "2.3.2"
tauri-plugin-prevent-default = "4.0.3"
[target.'cfg(target_os = "linux")'.dependencies]
shlex = "1.3.0"

View File

@@ -13,6 +13,7 @@ use std::{
use sysinfo::System;
use tauri::{AppHandle, Emitter, Manager};
use tauri_plugin_os::platform;
use tauri_plugin_prevent_default::Flags;
use tokio::io::AsyncReadExt;
use tokio::{io::AsyncWriteExt, time::timeout};
use zip::ZipArchive;
@@ -278,6 +279,9 @@ fn launch_game(
//if already running on macos, it'll auto take the user to that proccess
#[cfg(any(target_os = "windows", target_os = "linux"))]
{
use tauri_plugin_dialog::DialogExt;
use tauri_plugin_dialog::MessageDialogKind;
if !use_wine && is_running_by_path(&exe_path) {
app.dialog()
.message(format!(
@@ -326,6 +330,22 @@ fn launch_game(
pub fn run() {
#[allow(unused_variables)]
tauri::Builder::default()
.plugin(
tauri_plugin_prevent_default::Builder::new()
.with_flags(
Flags::FIND
| Flags::CARET_BROWSING
| Flags::DEV_TOOLS
| Flags::DOWNLOADS
| Flags::FOCUS_MOVE
| Flags::RELOAD
| Flags::SOURCE
| Flags::OPEN
| Flags::PRINT
| Flags::CONTEXT_MENU,
)
.build(),
)
.plugin(tauri_plugin_window_state::Builder::new().build())
.plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_notification::init())
@@ -340,11 +360,7 @@ pub fn run() {
.plugin(tauri_plugin_decorum::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![
download,
launch_game,
folder_size
])
.invoke_handler(tauri::generate_handler![download, launch_game, folder_size])
.setup(|app| {
#[cfg(target_os = "windows")]
{

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher",
"version": "1.6.0",
"version": "1.6.1",
"identifier": "xyz.lncvrt.games-launcher",
"build": {
"beforeDevCommand": "next dev --webpack",

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher",
"version": "1.6.0",
"version": "1.6.1",
"identifier": "xyz.lncvrt.games-launcher",
"build": {
"beforeDevCommand": "next dev --webpack",

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher",
"version": "1.6.0",
"version": "1.6.1",
"identifier": "xyz.lncvrt.games-launcher",
"build": {
"beforeDevCommand": "next dev --webpack",

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher",
"version": "1.6.0",
"version": "1.6.1",
"identifier": "xyz.lncvrt.games-launcher",
"build": {
"beforeDevCommand": "next dev --webpack",

View File

@@ -7,6 +7,7 @@ import { GetIconForUser } from '@/lib/BerryDash'
import Image from 'next/image'
import './styles.css'
import { useRouter } from 'next/navigation'
import { platform } from '@tauri-apps/plugin-os'
interface BaseEntry {
id: number
@@ -175,11 +176,17 @@ export default function BerryDashLeaderboards () {
</button>
</div>
</div>
<div className='box'>
<div
className={`box ${
platform() == 'windows'
? 'h-[calc(100vh-116px)]'
: 'h-[calc(100vh-84px)]'
}`}
>
{selected == -1 ? (
<>
<p className='text-center mt-4 text-xl'>Select a Leaderboard</p>
<div className='flex flex-col gap-2 mt-4 items-center justify-center'>
<p className='text-center mt-2 text-xl'>Select a Leaderboard</p>
<div className='flex flex-col gap-2 mt-2 items-center justify-center'>
<button
className='leaderboard-button'
onClick={() => setSelected(0)}
@@ -228,7 +235,13 @@ export default function BerryDashLeaderboards () {
<>
<div
className={`flex flex-col gap-2 overflow-y-auto ${
selected == 1 ? 'h-[calc(100vh-128px)]' : 'h-[calc(100vh-96px)]'
selected == 1
? platform() == 'windows'
? 'h-[calc(100vh-168px)]'
: 'h-[calc(100vh-136px)]'
: platform() == 'windows'
? 'h-[calc(100vh-128px)]'
: 'h-[calc(100vh-96px)]'
} px-1`}
>
{entries.map((item, index) => {
@@ -288,7 +301,7 @@ export default function BerryDashLeaderboards () {
<select
value={selectedBerryOption}
onChange={e => setSelectedBerryOption(Number(e.target.value))}
className='leaderboard-select mt-2'
className='leaderboard-select mt-0.75'
>
<option value='0'>Normal Berry</option>
<option value='1'>Poison Berry</option>

View File

@@ -1,12 +1,12 @@
@import "tailwindcss";
.box {
@apply bg-(--col1) border border-(--col3) rounded-lg w-auto p-1 h-[calc(100vh-84px)];
@apply bg-(--col1) border border-(--col3) rounded-lg w-auto p-1;
}
.leaderboard-button,
.leaderboard-select {
@apply bg-(--col2) hover:bg-(--col4) border border-(--col4) hover:border-(--col6) rounded-lg px-4 py-2 inline-block transition-all duration-200;
@apply bg-(--col2) hover:bg-(--col4) border border-(--col4) hover:border-(--col6) rounded-lg px-4 py-2 inline-block transition-all duration-200 cursor-pointer;
}
.leaderboard-entry {

View File

@@ -8,7 +8,7 @@ import { useRouter, useSearchParams } from 'next/navigation'
import { platform } from '@tauri-apps/plugin-os'
import { faWarning } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { ask, message } from '@tauri-apps/plugin-dialog'
import { ask } from '@tauri-apps/plugin-dialog'
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
import { writeVersionsConfig } from '@/lib/BazookaManager'
import { openFolder } from '@/lib/Util'
@@ -113,7 +113,7 @@ export default function Installs () {
<div className='downloads-container'>
<div
className={`downloads-scroll ${
platform() === 'windows'
platform() == 'windows'
? 'h-[calc(100vh-116px)]'
: 'h-[calc(100vh-84px)]'
}`}
@@ -129,7 +129,7 @@ export default function Installs () {
if (!info) return false
if (
platform() === 'linux' &&
platform() == 'linux' &&
info.wine &&
!normalConfig?.settings.useWineOnUnixWhenNeeded
)
@@ -165,7 +165,7 @@ export default function Installs () {
const info = getVersionInfo(v)
if (!info) return false
if (
platform() === 'linux' &&
platform() == 'linux' &&
info.wine &&
!normalConfig?.settings
.useWineOnUnixWhenNeeded
@@ -200,7 +200,7 @@ export default function Installs () {
const info = getVersionInfo(v)
if (!info) return false
if (
platform() === 'linux' &&
platform() == 'linux' &&
info.wine &&
!normalConfig?.settings.useWineOnUnixWhenNeeded
)
@@ -226,10 +226,60 @@ export default function Installs () {
entry
)
) {
await message(
"Can't launch game. Need revision update.",
{ title: 'Error launching game', kind: 'error' }
const answer = await ask(
'Before proceeding, if you do not want your installation directory wiped just yet, please backup the files to another directory. When you click "Yes", it will be wiped. Click "No" if you want to open the installation folder instead.',
{
title: 'Revision Update',
kind: 'warning'
}
)
if (answer) {
const answer2 = await ask(
'Are you sure you want to update? If you did not read the last popup, please go back and read it.',
{
title: 'Revision Update',
kind: 'warning'
}
)
if (!answer2) return
//open downloads popup
setPopupMode(1)
setShowPopup(true)
setFadeOut(false)
//uninstall
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedList = Object.fromEntries(
Object.entries(prev.list).filter(
([k]) => k !== entry
)
)
const updatedConfig = {
...prev,
list: updatedList
}
writeVersionsConfig(updatedConfig)
return updatedConfig
})
if (
await exists('game/' + entry, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove('game/' + entry, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
//reinstall
setSelectedVersionList([entry])
downloadVersions([entry])
} else {
openFolder(entry)
}
return
}
const verInfo = getVersionInfo(entry)
@@ -241,7 +291,7 @@ export default function Installs () {
executable: verInfo.executable,
displayName: verInfo.displayName,
useWine: !!(
platform() === 'linux' &&
platform() == 'linux' &&
verInfo.wine &&
normalConfig?.settings.useWineOnUnixWhenNeeded
),
@@ -279,8 +329,7 @@ export default function Installs () {
title='This version is using wine. It cannot be guarenteed to work fully and might not work at all.'
hidden={
!(
platform() === 'linux' &&
getVersionInfo(entry)?.wine
platform() == 'linux' && getVersionInfo(entry)?.wine
) ||
needsRevisionUpdate(
getVersionInfo(entry)?.lastRevision,
@@ -306,95 +355,6 @@ export default function Installs () {
<p>Needs revision update!</p>
</div>
</div>
<div className='flex gap-2 absolute right-0 bottom-0'>
<button
className='button'
onClick={e => {
e.stopPropagation()
setManagingVersion(entry)
setPopupMode(3)
setShowPopup(true)
setFadeOut(false)
}}
hidden={needsRevisionUpdate(
getVersionInfo(entry)?.lastRevision,
entry
)}
title='Click to view version info'
>
View Info
</button>
<button
className='button'
onClick={async e => {
e.stopPropagation()
const answer = await ask(
'Before proceeding, if you do not want your installation directory wiped just yet, please backup the files to another directory. When you click "Yes", it will be wiped. Click "No" if you want to open the installation folder instead.',
{
title: 'Revision Update',
kind: 'warning'
}
)
if (answer) {
const answer2 = await ask(
'Are you sure you want to update? If you did not read the last popup, please go back and read it.',
{
title: 'Revision Update',
kind: 'warning'
}
)
if (!answer2) return
//open downloads popup
setPopupMode(1)
setShowPopup(true)
setFadeOut(false)
//uninstall
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedList = Object.fromEntries(
Object.entries(prev.list).filter(
([k]) => k !== entry
)
)
const updatedConfig = {
...prev,
list: updatedList
}
writeVersionsConfig(updatedConfig)
return updatedConfig
})
if (
await exists('game/' + entry, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove('game/' + entry, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
//reinstall
setSelectedVersionList([entry])
downloadVersions([entry])
} else {
openFolder(entry)
}
}}
hidden={
!needsRevisionUpdate(
getVersionInfo(entry)?.lastRevision,
entry
)
}
title={'Click to update the game'}
>
Update
</button>
</div>
</div>
</div>
))

View File

@@ -32,11 +32,10 @@ import {
} from '@tauri-apps/plugin-notification'
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
import DownloadsPopup from '@/componets/popups/Downloads'
import VersionInfoPopup from '@/componets/popups/VersionInfo'
import ManagingVersionPopup from '@/componets/popups/ManageVersion'
import GamesDownloadPopup from '@/componets/popups/GamesDownload'
import VersionsDownloadPopup from '@/componets/popups/VersionsDownload'
import GamesDownloadPopup from '@/componets/popups/GamesDownload'
import DownloadsPopup from '@/componets/popups/Downloads'
import VersionVersionPopup from '@/componets/popups/VersionVersion'
const roboto = Roboto({
subsets: ['latin']
@@ -51,6 +50,7 @@ export default function RootLayout ({
const [loadingText, setLoadingText] = useState('Loading...')
const [outdated, setOutdated] = useState(false)
const [version, setVersion] = useState<string | null>(null)
const [platformName, setPlatformName] = useState<string | null>(null)
const [serverVersionList, setServerVersionList] =
useState<null | ServerVersionsResponse>(null)
@@ -89,7 +89,7 @@ export default function RootLayout ({
)
.filter(v => {
if (
platform() === 'linux' &&
platformName == 'linux' &&
v.wine &&
!normalConfig.settings.useWineOnUnixWhenNeeded
)
@@ -147,7 +147,7 @@ export default function RootLayout ({
if (!downloadedVersionsConfig || !serverVersionList) return null
const allowWine =
platform() !== 'linux' || normalConfig?.settings.useWineOnUnixWhenNeeded
platformName !== 'linux' || normalConfig?.settings.useWineOnUnixWhenNeeded
const installed = Object.keys(downloadedVersionsConfig.list).filter(v => {
const info = getVersionInfo(v)
@@ -172,8 +172,6 @@ export default function RootLayout ({
} else if (viewingInfoFromDownloads) {
setViewingInfoFromDownloads(false)
setPopupMode(0)
} else if (popupMode == 4) {
setPopupMode(3)
} else {
setFadeOut(true)
setTimeout(() => setShowPopup(false), 200)
@@ -234,10 +232,10 @@ export default function RootLayout ({
useEffect(() => {
;(async () => {
setPlatformName(platform())
const client = await app.getVersion()
setVersion(client)
if (process.env.NODE_ENV === 'production') {
setLoadingText('Checking latest version...')
try {
const response = await axios.get(
'https://games.lncvrt.xyz/api/launcher/latest'
@@ -251,7 +249,6 @@ export default function RootLayout ({
return
}
}
setLoadingText('Downloading version list...')
try {
const res = await axios.get(
`https://games.lncvrt.xyz/api/launcher/versions?platform=${platform()}&arch=${arch()}`
@@ -261,7 +258,6 @@ export default function RootLayout ({
setLoadingText('Failed to download versions list.')
return
}
setLoadingText('Loading configs...')
const normalConfig = await readNormalConfig()
const versionsConfig = await readVersionsConfig()
setDownloadedVersionsConfig(versionsConfig)
@@ -274,13 +270,6 @@ export default function RootLayout ({
})()
}, [])
useEffect(() => {
if (process.env.NODE_ENV !== 'production') return
const handler = (e: MouseEvent) => e.preventDefault()
document.addEventListener('contextmenu', handler)
return () => document.removeEventListener('contextmenu', handler)
}, [])
const downloadVersions = useCallback(
async (list: string[]): Promise<void> => {
if (list.length === 0) return
@@ -453,26 +442,38 @@ export default function RootLayout ({
}
>
{loading ? (
<div className='w-screen h-screen flex items-center justify-center'>
{outdated ? (
<div className='text-center'>
<p className='text-8xl mb-4'>Outdated Launcher!</p>
<p className='text-4xl mb-4'>
Please update to the latest version to continue.
</p>
<button
className='button'
onClick={() =>
openUrl('https://games.lncvrt.xyz/berrydash/download')
}
>
Download latest version
</button>
</div>
) : (
<p className='text-7xl text-center'>{loadingText}</p>
)}
</div>
<>
<div
className='relative z-2 w-screen border-b border-b-(--col3) h-8.25 bg-(--col1)'
hidden={platformName != 'windows'}
/>
<div
className={`w-screen ${
platformName == 'windows'
? 'h-[calc(100vh-64px)]'
: 'h-screen'
} flex items-center justify-center`}
>
{outdated ? (
<div className='text-center'>
<p className='text-8xl mb-4'>Outdated Launcher!</p>
<p className='text-4xl mb-4'>
Please update to the latest version to continue.
</p>
<button
className='button'
onClick={() =>
openUrl('https://games.lncvrt.xyz/berrydash/download')
}
>
Download latest version
</button>
</div>
) : (
<p className='text-7xl text-center'>{loadingText}</p>
)}
</div>
</>
) : (
<GlobalProvider
value={{
@@ -519,10 +520,8 @@ export default function RootLayout ({
>
<Sidebar />
<div
className='relative z-2 ml-59.75 w-[calc(100vw-239px)] border-b border-(--col3) h-8.25 bg-(--col1)'
style={{
display: platform() === 'windows' ? 'block' : 'none'
}}
className='relative z-2 ml-59.75 w-[calc(100vw-239px)] border-b border-b-(--col3) h-8.25 bg-(--col1)'
hidden={platformName != 'windows'}
/>
<div className='relative z-0'>
<main className='ml-60'>{children}</main>
@@ -567,9 +566,7 @@ export default function RootLayout ({
) : popupMode === 1 ? (
<DownloadsPopup />
) : popupMode === 2 ? (
<ManagingVersionPopup />
) : popupMode === 3 ? (
<VersionInfoPopup />
<VersionVersionPopup />
) : null}
</div>
</div>

View File

@@ -54,7 +54,7 @@ export default function Installs () {
<div className='downloads-container'>
<div
className={`downloads-scroll ${
platform() === 'windows'
platform() == 'windows'
? 'h-[calc(100vh-116px)]'
: 'h-[calc(100vh-84px)]'
}`}

View File

@@ -13,10 +13,6 @@
@apply bg-(--col3) border-(--col4);
}
.logo {
@apply flex items-center pl-2 h-10 w-60 pt-2;
}
.nav-links {
@apply flex flex-col px-4 space-y-1;
}

View File

@@ -59,15 +59,10 @@ export default function Sidebar () {
}}
></div>
<div
className='logo'
style={{
marginTop:
platform() == 'windows'
? '32px'
: platform() == 'macos'
? '28px'
: ''
}}
className={`flex items-center h-10 w-60 ${
(platform() == 'windows' ? 'pl-1 pt-1' : 'pl-2 pt-2') +
(platform() == 'macos' ? ' mt-7' : '')
}`}
onMouseDown={e => {
if (platform() != 'macos') return
if (e.buttons === 1) {
@@ -142,7 +137,7 @@ export default function Sidebar () {
if (!info) return false
if (
platform() === 'linux' &&
platform() == 'linux' &&
info.wine &&
!normalConfig?.settings.useWineOnUnixWhenNeeded
)

View File

@@ -58,7 +58,7 @@ export default function DownloadsPopup () {
v.hash_checking ? 'text-blue-300' : 'text-green-300'
} inline-block w-full text-center`}
>
{v.hash_checking ? 'Checking hash' : 'Finishing'}
{v.hash_checking ? 'Verifying file integerty' : 'Finishing'}
...
</span>
) : (

View File

@@ -1,106 +0,0 @@
import { useGlobal } from '@/app/GlobalProvider'
import { writeVersionsConfig } from '@/lib/BazookaManager'
import { openFolder } from '@/lib/Util'
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
export default function ManageVersionPopup () {
const {
getVersionInfo,
managingVersion,
closePopup,
setDownloadedVersionsConfig,
setManagingVersion,
downloadVersions,
setSelectedVersionList,
setPopupMode
} = useGlobal()
if (!managingVersion) return <></>
return (
<>
<p className='text-xl text-center'>
Manage {getVersionInfo(managingVersion)?.displayName}
</p>
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
<button
className='button btntheme2'
onClick={async () => {
closePopup()
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedList = Object.fromEntries(
Object.entries(prev.list).filter(([k]) => k !== managingVersion)
)
const updatedConfig = {
...prev,
list: updatedList
}
writeVersionsConfig(updatedConfig)
return updatedConfig
})
if (
await exists('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
}}
title='Click to uninstall this game. This will NOT remove any progress or any save files.'
>
Uninstall
</button>
<button
className='button btntheme2'
onClick={async () => {
//change popup to downloads
setManagingVersion(null)
setPopupMode(1)
//uninstall
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedList = Object.fromEntries(
Object.entries(prev.list).filter(([k]) => k !== managingVersion)
)
const updatedConfig = {
...prev,
list: updatedList
}
writeVersionsConfig(updatedConfig)
return updatedConfig
})
if (
await exists('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
//reinstall
setSelectedVersionList([managingVersion])
downloadVersions([managingVersion])
}}
title="Click to reinstall this game. This will NOT remove any progress or any save files. This WILL uninstall any modifications to the game's executable files."
>
Reinstall
</button>
<button
className='button btntheme2'
onClick={async () => openFolder(managingVersion)}
title="Click to browse the game's files."
>
Open Folder
</button>
</div>
</>
)
}

View File

@@ -14,24 +14,34 @@ import { invoke } from '@tauri-apps/api/core'
import { useEffect, useState } from 'react'
import prettyBytes from 'pretty-bytes'
import { message } from '@tauri-apps/plugin-dialog'
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
import { writeVersionsConfig } from '@/lib/BazookaManager'
import { openFolder } from '@/lib/Util'
export default function VersionInfoPopup () {
export default function VersionVersionPopup () {
const {
getGameInfo,
getVersionInfo,
managingVersion,
downloadedVersionsConfig,
viewingInfoFromDownloads
viewingInfoFromDownloads,
setManagingVersion,
closePopup,
setDownloadedVersionsConfig,
setPopupMode,
setSelectedVersionList,
downloadVersions
} = useGlobal()
const [versionSize, setVersionSize] = useState<number>(0)
useEffect(() => {
if (viewingInfoFromDownloads) return
invoke<string>('folder_size', {
version: managingVersion
}).then(size => {
setVersionSize(Number(size))
})
}, [managingVersion, setVersionSize])
}, [managingVersion, setVersionSize, viewingInfoFromDownloads])
if (!managingVersion || !downloadedVersionsConfig) return <></>
@@ -40,9 +50,7 @@ export default function VersionInfoPopup () {
return (
<>
<p className='text-xl text-center'>
Viewing info for {versionInfo?.displayName}
</p>
<p className='text-xl text-center'>Viewing {versionInfo?.displayName}</p>
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
<div
className='entry-info-item btntheme2'
@@ -86,12 +94,12 @@ export default function VersionInfoPopup () {
</div>
<div
className='entry-info-item btntheme2'
hidden={viewingInfoFromDownloads || versionSize === null}
hidden={viewingInfoFromDownloads}
>
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
<p>
Size on disk:{' '}
{versionSize > 0
{versionSize && versionSize > 0
? prettyBytes(versionSize, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
@@ -99,7 +107,10 @@ export default function VersionInfoPopup () {
: 'N/A'}
</p>
</div>
<div className='entry-info-item btntheme2' hidden={!versionInfo}>
<div
className='entry-info-item btntheme2'
hidden={!viewingInfoFromDownloads}
>
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
<p>
Size when downloaded (zipped):{' '}
@@ -125,6 +136,88 @@ export default function VersionInfoPopup () {
<p>View Changelog</p>
<FontAwesomeIcon icon={faArrowUpRightFromSquare} color='lightgray' />
</div>
<div
className='entry-info-item btntheme2'
onClick={async () => openFolder(managingVersion)}
title="Click to browse the game's files."
hidden={viewingInfoFromDownloads}
>
Open Folder
<FontAwesomeIcon icon={faArrowUpRightFromSquare} color='lightgray' />
</div>
<div
className='entry-info-item btntheme2'
onClick={async () => {
closePopup()
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedList = Object.fromEntries(
Object.entries(prev.list).filter(([k]) => k !== managingVersion)
)
const updatedConfig = {
...prev,
list: updatedList
}
writeVersionsConfig(updatedConfig)
return updatedConfig
})
if (
await exists('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
}}
title='Click to uninstall this game. This will NOT remove any progress or any save files.'
hidden={viewingInfoFromDownloads}
>
Uninstall
</div>
<div
className='entry-info-item btntheme2'
onClick={async () => {
//change popup to downloads
setManagingVersion(null)
setPopupMode(1)
//uninstall
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedList = Object.fromEntries(
Object.entries(prev.list).filter(([k]) => k !== managingVersion)
)
const updatedConfig = {
...prev,
list: updatedList
}
writeVersionsConfig(updatedConfig)
return updatedConfig
})
if (
await exists('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
//reinstall
setSelectedVersionList([managingVersion])
downloadVersions([managingVersion])
}}
title="Click to reinstall this game. This will NOT remove any progress or any save files. This WILL uninstall any modifications to the game's executable files."
hidden={viewingInfoFromDownloads}
>
Reinstall
</div>
</div>
</>
)

View File

@@ -12,10 +12,8 @@ export default function VersionsDownloadPopup () {
selectedGame,
setViewingInfoFromDownloads,
downloadedVersionsConfig,
downloadProgress,
downloadVersions,
getGameInfo,
downloadQueue
getGameInfo
} = useGlobal()
if (!selectedGame) return <></>
@@ -66,7 +64,7 @@ export default function VersionsDownloadPopup () {
onClick={() => {
setManagingVersion(v.id)
setViewingInfoFromDownloads(true)
setPopupMode(3)
setPopupMode(2)
}}
title='Click to view version info'
>
@@ -81,25 +79,19 @@ export default function VersionsDownloadPopup () {
onClick={() => {
if (downloadedVersionsConfig) {
downloadVersions(selectedVersionList)
setPopupMode(1)
}
}}
disabled={selectedVersionList.length === 0}
disabled={selectedVersionList.length == 0}
title={
selectedVersionList.length === 0
selectedVersionList.length == 0
? 'Select at least one version to download'
: downloadProgress.length > 0 || downloadQueue.length > 0
? `Add ${selectedVersionList.length} version${
selectedVersionList.length == 1 ? '' : 's'
} to download queue`
: `Download ${selectedVersionList.length} version${
selectedVersionList.length == 1 ? '' : 's'
} of ${getGameInfo(selectedGame)?.name}`
}
>
{downloadProgress.length > 0 || downloadQueue.length > 0
? `Add ${selectedVersionList.length} to Queue`
: `Download ${selectedVersionList.length}`}{' '}
version
Download {selectedVersionList.length} version
{selectedVersionList.length == 1 ? '' : 's'}
</button>
<button

View File

@@ -11,11 +11,20 @@ export const openFolder = async (name: string) => {
const folderExists = await exists(relativePath, {
baseDir: BaseDirectory.AppLocalData
})
if (!folderExists) {
await message(`Game folder "${absolutePath}" not found.`, {
title: 'Folder not found',
kind: 'error'
})
return
}
const folderStat = await stat(relativePath, {
baseDir: BaseDirectory.AppLocalData
})
if (!folderExists || folderStat.isFile) {
if (folderStat.isFile) {
await message(`Game folder "${absolutePath}" not found.`, {
title: 'Folder not found',
kind: 'error'