Compare commits

...
6 Commits
15 changed files with 195 additions and 200 deletions

No files matched your search

+9 -9
View File
@@ -5,9 +5,9 @@
"": {
"name": "lncvrt-games-launcher",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "7.1.0",
"@fortawesome/free-brands-svg-icons": "7.1.0",
"@fortawesome/free-solid-svg-icons": "7.1.0",
"@fortawesome/fontawesome-svg-core": "7.2.0",
"@fortawesome/free-brands-svg-icons": "7.2.0",
"@fortawesome/free-solid-svg-icons": "7.2.0",
"@fortawesome/react-fontawesome": "3.2.0",
"@tauri-apps/api": "2.10.1",
"@tauri-apps/plugin-clipboard-manager": "2.3.2",
@@ -28,7 +28,7 @@
"@tailwindcss/postcss": "4.1.18",
"@tauri-apps/cli": "2.10.0",
"@types/crypto-js": "4.2.2",
"@types/node": "25.2.2",
"@types/node": "25.2.3",
"@types/react": "19.2.13",
"@types/react-dom": "19.2.3",
"crypto-js": "4.2.0",
@@ -98,13 +98,13 @@
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="],
"@fortawesome/fontawesome-common-types": ["@fortawesome/fontawesome-common-types@7.1.0", "", {}, "sha512-l/BQM7fYntsCI//du+6sEnHOP6a74UixFyOYUyz2DLMXKx+6DEhfR3F2NYGE45XH1JJuIamacb4IZs9S0ZOWLA=="],
"@fortawesome/fontawesome-common-types": ["@fortawesome/fontawesome-common-types@7.2.0", "", {}, "sha512-IpR0bER9FY25p+e7BmFH25MZKEwFHTfRAfhOyJubgiDnoJNsSvJ7nigLraHtp4VOG/cy8D7uiV0dLkHOne5Fhw=="],
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@7.1.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.1.0" } }, "sha512-fNxRUk1KhjSbnbuBxlWSnBLKLBNun52ZBTcs22H/xEEzM6Ap81ZFTQ4bZBxVQGQgVY0xugKGoRcCbaKjLQ3XZA=="],
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@7.2.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.2.0" } }, "sha512-6639htZMjEkwskf3J+e6/iar+4cTNM9qhoWuRfj9F3eJD6r7iCzV1SWnQr2Mdv0QT0suuqU8BoJCZUyCtP9R4Q=="],
"@fortawesome/free-brands-svg-icons": ["@fortawesome/free-brands-svg-icons@7.1.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.1.0" } }, "sha512-9byUd9bgNfthsZAjBl6GxOu1VPHgBuRUP9juI7ZoM98h8xNPTCTagfwUFyYscdZq4Hr7gD1azMfM9s5tIWKZZA=="],
"@fortawesome/free-brands-svg-icons": ["@fortawesome/free-brands-svg-icons@7.2.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.2.0" } }, "sha512-VNG8xqOip1JuJcC3zsVsKRQ60oXG9+oYNDCosjoU/H9pgYmLTEwWw8pE0jhPz/JWdHeUuK6+NQ3qsM4gIbdbYQ=="],
"@fortawesome/free-solid-svg-icons": ["@fortawesome/free-solid-svg-icons@7.1.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.1.0" } }, "sha512-Udu3K7SzAo9N013qt7qmm22/wo2hADdheXtBfxFTecp+ogsc0caQNRKEb7pkvvagUGOpG9wJC1ViH6WXs8oXIA=="],
"@fortawesome/free-solid-svg-icons": ["@fortawesome/free-solid-svg-icons@7.2.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.2.0" } }, "sha512-YTVITFGN0/24PxzXrwqCgnyd7njDuzp5ZvaCx5nq/jg55kUYd94Nj8UTchBdBofi/L0nwRfjGOg0E41d2u9T1w=="],
"@fortawesome/react-fontawesome": ["@fortawesome/react-fontawesome@3.2.0", "", { "peerDependencies": { "@fortawesome/fontawesome-svg-core": "~6 || ~7", "react": "^18.0.0 || ^19.0.0" } }, "sha512-E9Gu1hqd6JussVO26EC4WqRZssXMnQr2ol7ZNWkkFOH8jZUaxDJ9Z9WF9wIVkC+kJGXUdY3tlffpDwEKfgQrQw=="],
@@ -290,7 +290,7 @@
"@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
"@types/node": ["@types/node@25.2.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ=="],
"@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="],
"@types/react": ["@types/react@19.2.13", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ=="],
+5 -5
View File
@@ -1,7 +1,7 @@
{
"name": "lncvrt-games-launcher",
"private": true,
"version": "1.5.7",
"version": "1.5.8",
"type": "module",
"scripts": {
"dev": "next dev",
@@ -11,9 +11,9 @@
"tauri": "tauri"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "7.1.0",
"@fortawesome/free-brands-svg-icons": "7.1.0",
"@fortawesome/free-solid-svg-icons": "7.1.0",
"@fortawesome/fontawesome-svg-core": "7.2.0",
"@fortawesome/free-brands-svg-icons": "7.2.0",
"@fortawesome/free-solid-svg-icons": "7.2.0",
"@fortawesome/react-fontawesome": "3.2.0",
"@tauri-apps/api": "2.10.1",
"@tauri-apps/plugin-clipboard-manager": "2.3.2",
@@ -34,7 +34,7 @@
"@tailwindcss/postcss": "4.1.18",
"@tauri-apps/cli": "2.10.0",
"@types/crypto-js": "4.2.2",
"@types/node": "25.2.2",
"@types/node": "25.2.3",
"@types/react": "19.2.13",
"@types/react-dom": "19.2.3",
"crypto-js": "4.2.0",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "lncvrt-games-launcher"
version = "1.5.7"
version = "1.5.8"
authors = ["Lncvrt"]
edition = "2024"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher",
"version": "1.5.7",
"version": "1.5.8",
"identifier": "xyz.lncvrt.games-launcher",
"build": {
"beforeDevCommand": "next dev",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher",
"version": "1.5.7",
"version": "1.5.8",
"identifier": "xyz.lncvrt.games-launcher",
"build": {
"beforeDevCommand": "next dev",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher",
"version": "1.5.7",
"version": "1.5.8",
"identifier": "xyz.lncvrt.games-launcher",
"build": {
"beforeDevCommand": "next dev",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher",
"version": "1.5.7",
"version": "1.5.8",
"identifier": "xyz.lncvrt.games-launcher",
"build": {
"beforeDevCommand": "next dev",
+2
View File
@@ -43,6 +43,8 @@ type GlobalCtxType = {
viewingInfoFromDownloads: boolean
version: string | null
downloadVersions: (list: string[]) => Promise<void>
category: number
setCategory: Dispatch<SetStateAction<number>>
}
const GlobalCtx = createContext<GlobalCtxType | null>(null)
+75 -8
View File
@@ -8,16 +8,18 @@ import {
faCog,
faDownload,
faGamepad,
faHexagonNodes
faHexagonNodes,
faLayerGroup
} from '@fortawesome/free-solid-svg-icons'
import { faDiscord } from '@fortawesome/free-brands-svg-icons'
import { platform } from '@tauri-apps/plugin-os'
import { useGlobal } from '../GlobalProvider'
import Image from 'next/image'
import Link from 'next/link'
import { usePathname, useSearchParams } from 'next/navigation'
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { Lexend } from 'next/font/google'
import React from 'react'
const lexend = Lexend({
subsets: ['latin']
@@ -30,11 +32,16 @@ export default function Sidebar () {
setShowPopup,
setPopupMode,
setFadeOut,
downloadProgress
downloadProgress,
downloadedVersionsConfig,
getVersionInfo,
category,
setCategory
} = useGlobal()
const pathname = usePathname()
const params = useSearchParams()
const router = useRouter()
return (
<aside className='sidebar'>
@@ -92,10 +99,9 @@ export default function Sidebar () {
return a.id - b.id
})
.map(i => (
<Link
key={i.id}
<React.Fragment key={i.id}>
<div
draggable={false}
href={'/game?id=' + i.id}
className={`link ${
pathname === '/game' && Number(params.get('id') || 0) == i.id
? 'active'
@@ -107,12 +113,73 @@ export default function Sidebar () {
? ''
: 'hidden'
}`}
onClick={() => {
setCategory(-1)
router.push('/game?id=' + i.id)
}}
>
<div className='flex items-center'>
<FontAwesomeIcon
icon={
Object.entries(i.categoryNames).length > 0
? faLayerGroup
: faGamepad
}
className='mr-1'
/>
<span className='truncate max-w-full'>{i.name}</span>
</div>
</div>
{Object.entries(i.categoryNames)
.sort(([a], [b]) => Number(b) - Number(a))
.filter(([key]) => {
const count = Object.keys(
downloadedVersionsConfig?.list ?? {}
).filter(v => {
const info = getVersionInfo(v)
if (!info) return false
if (
platform() === 'linux' &&
info.wine &&
!normalConfig?.settings.useWineOnUnixWhenNeeded
)
return false
return info.game === i.id && info.category === Number(key)
}).length
return count >= 1
})
.map(([key, value]) => (
<div
key={`${i.id}-${key}`}
draggable={false}
className={`link ${
pathname === '/game' &&
Number(params.get('id') || 0) == i.id &&
category == Number(key)
? 'active'
: ''
} ml-auto w-47.5 ${
normalConfig?.settings.alwaysShowGamesInSidebar ||
pathname === '/' ||
pathname === '/game'
? ''
: 'hidden'
}`}
onClick={() => {
setCategory(Number(key))
router.push('/game?id=' + i.id)
}}
>
<div className='flex items-center'>
<FontAwesomeIcon icon={faGamepad} className='mr-1' />
<span className='truncate max-w-full'>{i.name}</span>
<span className='truncate max-w-full'>{value}</span>
</div>
</Link>
</div>
))}
</React.Fragment>
))}
<Link
draggable={false}
-27
View File
@@ -1,27 +0,0 @@
'use client'
import { useGlobal } from '../GlobalProvider'
export default function VersionChangelog () {
const { getVersionInfo, managingVersion, downloadedVersionsConfig } =
useGlobal()
if (!managingVersion || !downloadedVersionsConfig) return <></>
const versionInfo = getVersionInfo(managingVersion)
return (
<>
<p className='text-xl text-center'>
Viewing changelog for {versionInfo?.displayName}
</p>
<div className='popup-content text-center p-2'>
<span
className='whitespace-pre-wrap select-text'
dangerouslySetInnerHTML={{
__html: versionInfo?.changelog ? atob(versionInfo.changelog) : ''
}}
/>
</div>
</>
)
}
+9 -3
View File
@@ -13,6 +13,7 @@ import { useGlobal } from '../GlobalProvider'
import { invoke } from '@tauri-apps/api/core'
import { useEffect, useState } from 'react'
import prettyBytes from 'pretty-bytes'
import { message } from '@tauri-apps/plugin-dialog'
export default function VersionInfo () {
const {
@@ -20,8 +21,7 @@ export default function VersionInfo () {
getVersionInfo,
managingVersion,
downloadedVersionsConfig,
viewingInfoFromDownloads,
setPopupMode
viewingInfoFromDownloads
} = useGlobal()
const [versionSize, setVersionSize] = useState<number>(0)
@@ -113,7 +113,13 @@ export default function VersionInfo () {
</div>
<div
className='entry-info-item btntheme2'
onClick={() => setPopupMode(4)}
onClick={async () => {
if (!versionInfo) return
await message(atob(versionInfo.changelog), {
title: 'Changelog for ' + versionInfo.displayName,
kind: 'info'
})
}}
hidden={!versionInfo?.changelog}
>
<p>View Changelog</p>
-105
View File
@@ -1,105 +0,0 @@
'use client'
import { invoke } from '@tauri-apps/api/core'
import { useGlobal } from '../GlobalProvider'
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
import { writeVersionsConfig } from '../util/BazookaManager'
import { useState } from 'react'
export default function VersionUpdateWarning () {
const [confirmed, setConfirmed] = useState<-1 | 0>(-1)
const {
managingVersion,
setDownloadedVersionsConfig,
setManagingVersion,
setPopupMode,
setSelectedVersionList,
downloadVersions
} = useGlobal()
if (!managingVersion) return <p>Error</p>
return (
<>
<p className='text-xl text-center'>Warning!</p>
<div className='popup-content text-center p-2 relative'>
<p className='mb-2'>
Before proceeding, please note that any modifications to the
installation directory <b>(NOT THE SAVE DATA)</b> will be completely
wiped/reset.
</p>
<p>
If you do not want your installation directory wiped just yet, please
backup the files to another directory. When you click update, it will
be wiped.
</p>
<p className='my-2'>
Updating will have the same effect as clicking the uninstall button
then installing again.
</p>
<p>Revisions are not a frequent thing and rarely ever happen.</p>
<div className='flex flex-row gap-2 absolute bottom-2 left-1/2 -translate-x-1/2 w-max'>
<button
className='button btntheme2'
onClick={async () =>
invoke('open_folder', {
name: managingVersion
})
}
title="Click to browse the game's files."
>
Open Folder / Installation directory
</button>
<button
className='button btntheme2'
onClick={async () => {
if (confirmed == -1) {
setConfirmed(0)
return
}
//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 update the game'
>
<span className='text-red-500 font-bold underline'>
{confirmed == -1 ? 'Update' : 'Are you sure?'}
</span>
</button>
</div>
</div>
</>
)
}
+75 -14
View File
@@ -1,6 +1,6 @@
'use client'
import { useEffect, useState } from 'react'
import { useEffect } from 'react'
import '../Installs.css'
import { invoke } from '@tauri-apps/api/core'
import { useGlobal } from '../GlobalProvider'
@@ -8,6 +8,9 @@ import { 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 } from '@tauri-apps/plugin-dialog'
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
import { writeVersionsConfig } from '../util/BazookaManager'
export default function Installs () {
const {
@@ -17,12 +20,17 @@ export default function Installs () {
setFadeOut,
setSelectedVersionList,
downloadedVersionsConfig,
downloadProgress,
normalConfig,
setManagingVersion,
getVersionInfo,
getGameInfo,
setSelectedGame,
serverVersionList
serverVersionList,
category,
setCategory,
setDownloadedVersionsConfig,
downloadVersions
} = useGlobal()
const params = useSearchParams()
@@ -30,9 +38,6 @@ export default function Installs () {
const id = Number(params.get('id') || 0)
const game = serverVersionList?.games.find(g => g.id === id)
const [category, setCategory] = useState<number>(-1)
const [lastId, setLastId] = useState(id)
useEffect(() => {
if (!showPopup) return
setSelectedVersionList([])
@@ -40,11 +45,6 @@ export default function Installs () {
if (!id || !game) return <p>Invalid game</p>
if (lastId !== id) {
setLastId(id)
setCategory(-1)
}
const needsRevisionUpdate = (
lastRevision: number | undefined,
version: string
@@ -302,6 +302,10 @@ export default function Installs () {
!(
platform() === 'linux' &&
getVersionInfo(entry)?.wine
) ||
needsRevisionUpdate(
getVersionInfo(entry)?.lastRevision,
entry
)
}
onClick={e => e.stopPropagation()}
@@ -396,12 +400,64 @@ export default function Installs () {
</button>
<button
className='button'
onClick={e => {
onClick={async e => {
e.stopPropagation()
setManagingVersion(entry)
setPopupMode(5)
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 {
invoke('open_folder', {
name: entry
})
}
}}
hidden={
!needsRevisionUpdate(
@@ -409,7 +465,12 @@ export default function Installs () {
entry
)
}
title='Click to update the game'
disabled={downloadProgress.length != 0}
title={
downloadProgress.length != 0
? 'Cannot update while downloads are in progress '
: 'Click to update the game'
}
>
Update
</button>
+6 -19
View File
@@ -44,9 +44,7 @@ import {
isPermissionGranted,
requestPermission
} from '@tauri-apps/plugin-notification'
import VersionChangelog from './componets/VersionChangelog'
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
import VersionUpdateWarning from './componets/VersionUpdateWarning'
const roboto = Roboto({
subsets: ['latin']
@@ -82,6 +80,8 @@ export default function RootLayout ({
useState<boolean>(false)
const [selectedGame, setSelectedGame] = useState<number | null>(null)
const [category, setCategory] = useState<number>(-1)
const pathname = usePathname()
const revisionCheck = useRef(false)
@@ -101,6 +101,7 @@ export default function RootLayout ({
return false
if (game && v.game != game) return false
if (category != -1 && v.category != category) return false
if (downloadProgress.length != 0) {
return !downloadProgress.some(d => d.version === v.id)
}
@@ -480,7 +481,9 @@ export default function RootLayout ({
getVersionsAmountData,
viewingInfoFromDownloads,
version,
downloadVersions
downloadVersions,
category,
setCategory
}}
>
<div
@@ -885,22 +888,6 @@ export default function RootLayout ({
No version selected
</p>
)
) : popupMode === 4 ? (
managingVersion && downloadedVersionsConfig ? (
<VersionChangelog />
) : (
<p className='text-xl text-center'>
No version selected
</p>
)
) : popupMode === 5 ? (
managingVersion && downloadedVersionsConfig ? (
<VersionUpdateWarning />
) : (
<p className='text-xl text-center'>
No version selected
</p>
)
) : null}
{popupMode == 0 &&
selectedGame &&
+9 -5
View File
@@ -3,7 +3,6 @@
import { useEffect } from 'react'
import './Installs.css'
import { useGlobal } from './GlobalProvider'
import Link from 'next/link'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import {
faCheck,
@@ -24,7 +23,8 @@ export default function Installs () {
normalConfig,
setSelectedGame,
getListOfGames,
getVersionsAmountData
getVersionsAmountData,
setCategory
} = useGlobal()
const router = useRouter()
@@ -80,6 +80,7 @@ export default function Installs () {
}
onClick={() => {
if (normalConfig?.settings.useLegacyInteractButtons) return
setCategory(-1)
router.push('/game?id=' + i.id)
}}
>
@@ -128,14 +129,17 @@ export default function Installs () {
</div>
</div>
<Link
<div
className='button absolute right-0 bottom-0'
href={'/game?id=' + i.id}
hidden={!normalConfig?.settings.useLegacyInteractButtons}
title='Click to view game installs'
onClick={() => {
setCategory(-1)
router.push('/game?id=' + i.id)
}}
>
Installs
</Link>
</div>
</div>
</div>
))