Improvements & add home page (not even made just going to work on it)

This commit is contained in:
Lncvrt committed 2026-06-05 09:58:54 -07:00
1 parent f4e389915f
commit 1407611dcc
8 files changed
+360 -362

No files matched your search

-13
View File
@@ -1,13 +0,0 @@
@import "tailwindcss";
.downloads-container {
@apply flex justify-center;
}
.downloads-scroll {
@apply bg-(--col1) border border-(--col3) rounded-lg overflow-y-auto w-full;
}
.downloads-entry {
@apply flex justify-between items-center m-2 p-2 rounded-lg text-gray-200 text-lg transition-colors bg-(--col2) hover:bg-(--col3) border border-(--col4) hover:border-(--col5) cursor-pointer;
}
+181 -189
View File
@@ -1,6 +1,5 @@
'use client'
import '../Installs.css'
import { useEffect } from 'react'
import { useGlobal } from '@/providers/GlobalProvider'
import { useSearchParams } from 'next/navigation'
@@ -11,7 +10,7 @@ import { ask } from '@tauri-apps/plugin-dialog'
import { BaseDirectory, exists } from '@tauri-apps/plugin-fs'
import { WebviewWindow } from '@tauri-apps/api/webviewWindow'
export default function Installs () {
export default function GamePage () {
const {
versionsList,
serverVersionList,
@@ -186,211 +185,204 @@ export default function Installs () {
</button>
</div>
</div>
<div className='downloads-container'>
<div
className={`downloads-scroll ${
platform() == 'windows'
? 'h-[calc(100vh-116px)]'
: 'h-[calc(100vh-84px)]'
}`}
>
{category == -1 &&
Object.entries(game.categoryNames)
.filter(([key]) => {
const count = Object.keys(versionsList).filter(v => {
const info = serverVersionList?.versions.find(
vf => vf.id == v
)
if (!info) return false
if (platform() == 'linux' && info.wine && !linuxUseWine)
return false
return info.game === id && info.category === Number(key)
}).length
return count >= 1
})
.sort(([a], [b]) => Number(b) - Number(a))
.map(([key, value]) => {
return (
<div
key={key}
className='downloads-entry'
title='Click to view category.'
onClick={() => setCategory(Number(key))}
>
<div className='h-18 w-screen relative'>
{developerMode && (
<p className='absolute bottom-0 right-0 text-sm'>
{key}
</p>
)}
<p className='text-2xl'>{value}</p>
<div
className='entry-info-item flex absolute left-0 bottom-0'
title='The amount of versions installed of this game in installed/installable format.'
onClick={e => e.stopPropagation()}
>
<p>
{(() => {
const count =
Object.keys(versionsList).filter(v => {
const info = serverVersionList?.versions.find(
vf => vf.id == v
)
if (!info) return false
if (
platform() == 'linux' &&
info.wine &&
!linuxUseWine
)
return false
return (
info.game === id &&
info.category == Number(key)
)
}).length ?? 0
return `${count} install${count === 1 ? '' : 's'}`
})()}
</p>
</div>
</div>
</div>
)
})}
{filteredVersions.length === 0 &&
(category !== -1 ||
Object.keys(versionsList).filter(v => {
<div
className={`main-container main-flex ${
platform() == 'windows'
? 'h-[calc(100vh-116px)]'
: 'h-[calc(100vh-84px)]'
}`}
>
{category == -1 &&
Object.entries(game.categoryNames)
.filter(([key]) => {
const count = Object.keys(versionsList).filter(v => {
const info = serverVersionList?.versions.find(vf => vf.id == v)
if (!info) return false
if (platform() == 'linux' && info.wine && !linuxUseWine)
return false
return info.game === id
}).length === 0) && (
<div className='flex justify-center items-center h-full'>
<p className='text-3xl'>No versions installed</p>
</div>
)}
{filteredVersions
.sort((a, b) => {
const infoA = serverVersionList?.versions.find(vf => vf.id == a)
const infoB = serverVersionList?.versions.find(vf => vf.id == b)
if (!infoA || !infoB) return 0
return infoB.place - infoA.place
})
.map(v => {
const versionInfo = serverVersionList?.versions.find(
vf => vf.id == v
)
if (!versionInfo) return
return info.game === id && info.category === Number(key)
}).length
return count >= 1
})
.sort(([a], [b]) => Number(b) - Number(a))
.map(([key, value]) => {
return (
<div
key={v}
className='downloads-entry'
title='Click to launch game. Right-click to manage this version install.'
onClick={async () => await launchGame(versionInfo)}
onContextMenu={e => {
e.preventDefault()
setManagingVersion(v)
setPopupMode(2)
setShowPopup(true)
setFadeOut(false)
}}
key={key}
className='main-entry'
title='Click to view category.'
onClick={() => setCategory(Number(key))}
>
<div className='h-18 w-screen relative'>
{developerMode && (
<p className='absolute bottom-0 right-0 text-sm'>
{versionInfo.id}
<p className='absolute bottom-0 right-0 text-sm'>{key}</p>
)}
<p className='text-2xl'>{value}</p>
<div
className='entry-info-item flex absolute left-0 bottom-0'
title='The amount of versions installed of this game in installed/installable format.'
onClick={e => e.stopPropagation()}
>
<p>
{(() => {
const count =
Object.keys(versionsList).filter(v => {
const info = serverVersionList?.versions.find(
vf => vf.id == v
)
if (!info) return false
if (
platform() == 'linux' &&
info.wine &&
!linuxUseWine
)
return false
return (
info.game === id && info.category == Number(key)
)
}).length ?? 0
return `${count} install${count === 1 ? '' : 's'}`
})()}
</p>
)}
<p className='text-2xl'>{versionInfo.displayName}</p>
<div className='flex gap-2 absolute left-0 bottom-0'>
<div
className='entry-info-item'
title='The date the game was installed.'
>
<p>
Installed{' '}
{new Intl.DateTimeFormat(undefined).format(
versionsList[v]
)}
</p>
</div>
{platform() == 'linux' &&
versionInfo.wine &&
!needsRevisionUpdate(versionInfo.lastRevision, v) && (
<div
className='entry-info-item'
title='This version is using wine. It cannot be guarenteed to work fully and might not work at all.'
>
<FontAwesomeIcon icon={faWarning} color='#ffc800' />
<p>Uses wine</p>
</div>
)}
{needsRevisionUpdate(versionInfo.lastRevision, v) && (
<div className='entry-info-item'>
<FontAwesomeIcon icon={faWarning} color='#ffc800' />
<p>Needs revision update!</p>
</div>
)}
</div>
{versionInfo.modSupportDownload && (
<button
className='absolute right-0 bottom-0 button'
title='Click to manage mods for this game!'
onClick={async e => {
e.stopPropagation()
if (
!(await exists(
customDataLocation
? customDataLocation + '/'
: null + 'game/' + v + '/BepInEx',
{
baseDir: customDataLocation
? undefined
: BaseDirectory.AppLocalData
}
))
) {
if (
(await ask(
"You don't have BepInEx (the mod loader for Unity Games), would you like to install it now? It is about 1MB in size. If you choose yes, it will download the recommended BepInEx version for " +
versionInfo.displayName +
'.',
{ title: 'BepInEx not found!', kind: 'error' }
)) &&
!downloadProgress.find(d => d.version == v)
) {
await downloadVersions([
{
id: v,
type: 1
}
])
}
return
}
setManagingVersion(v)
setPopupMode(3)
setShowPopup(true)
setFadeOut(false)
}}
>
Mod Manager
</button>
)}
</div>
</div>
)
})}
</div>
{filteredVersions.length === 0 &&
(category !== -1 ||
Object.keys(versionsList).filter(v => {
const info = serverVersionList?.versions.find(vf => vf.id == v)
if (!info) return false
if (platform() == 'linux' && info.wine && !linuxUseWine)
return false
return info.game === id
}).length === 0) && (
<div className='flex justify-center items-center h-full'>
<p className='text-3xl'>No versions installed</p>
</div>
)}
{filteredVersions
.sort((a, b) => {
const infoA = serverVersionList?.versions.find(vf => vf.id == a)
const infoB = serverVersionList?.versions.find(vf => vf.id == b)
if (!infoA || !infoB) return 0
return infoB.place - infoA.place
})
.map(v => {
const versionInfo = serverVersionList?.versions.find(
vf => vf.id == v
)
if (!versionInfo) return
return (
<div
key={v}
className='main-entry'
title='Click to launch game. Right-click to manage this version install.'
onClick={async () => await launchGame(versionInfo)}
onContextMenu={e => {
e.preventDefault()
setManagingVersion(v)
setPopupMode(2)
setShowPopup(true)
setFadeOut(false)
}}
>
<div className='h-18 w-screen relative'>
{developerMode && (
<p className='absolute bottom-0 right-0 text-sm'>
{versionInfo.id}
</p>
)}
<p className='text-2xl'>{versionInfo.displayName}</p>
<div className='flex gap-2 absolute left-0 bottom-0'>
<div
className='entry-info-item'
title='The date the game was installed.'
>
<p>
Installed{' '}
{new Intl.DateTimeFormat(undefined).format(
versionsList[v]
)}
</p>
</div>
{platform() == 'linux' &&
versionInfo.wine &&
!needsRevisionUpdate(versionInfo.lastRevision, v) && (
<div
className='entry-info-item'
title='This version is using wine. It cannot be guarenteed to work fully and might not work at all.'
>
<FontAwesomeIcon icon={faWarning} color='#ffc800' />
<p>Uses wine</p>
</div>
)}
{needsRevisionUpdate(versionInfo.lastRevision, v) && (
<div className='entry-info-item'>
<FontAwesomeIcon icon={faWarning} color='#ffc800' />
<p>Needs revision update!</p>
</div>
)}
</div>
{versionInfo.modSupportDownload && (
<button
className='absolute right-0 bottom-0 button'
title='Click to manage mods for this game!'
onClick={async e => {
e.stopPropagation()
if (
!(await exists(
customDataLocation
? customDataLocation + '/'
: null + 'game/' + v + '/BepInEx',
{
baseDir: customDataLocation
? undefined
: BaseDirectory.AppLocalData
}
))
) {
if (
(await ask(
"You don't have BepInEx (the mod loader for Unity Games), would you like to install it now? It is about 1MB in size. If you choose yes, it will download the recommended BepInEx version for " +
versionInfo.displayName +
'.',
{ title: 'BepInEx not found!', kind: 'error' }
)) &&
!downloadProgress.find(d => d.version == v)
) {
await downloadVersions([
{
id: v,
type: 1
}
])
}
return
}
setManagingVersion(v)
setPopupMode(3)
setShowPopup(true)
setFadeOut(false)
}}
>
Mod Manager
</button>
)}
</div>
</div>
)
})}
</div>
</div>
)
+147
View File
@@ -0,0 +1,147 @@
'use client'
import { useEffect } from 'react'
import { useGlobal } from '@/providers/GlobalProvider'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import {
faCheck,
faShieldHalved,
faWarning
} from '@fortawesome/free-solid-svg-icons'
import { platform } from '@tauri-apps/plugin-os'
import { useRouter } from 'next/navigation'
export default function GamesPage () {
const {
versionsList,
serverVersionList,
showPopup,
setShowPopup,
setPopupMode,
setFadeOut,
setSelectedVersionList,
setSelectedGame,
setCategory,
setManagingGame,
developerMode
} = useGlobal()
const router = useRouter()
useEffect(() => {
if (!showPopup) return
setSelectedVersionList([])
}, [setSelectedVersionList, showPopup])
const filteredGames =
serverVersionList?.games.filter(g =>
serverVersionList.versions
.filter(v => v.game === g.id)
.some(v => Object.keys(versionsList).includes(v.id))
) ?? []
return (
<div className='mx-4 mt-4'>
<div className='flex justify-between items-center mb-4'>
<p className='text-3xl'>Games</p>
<button
className='button btntheme1'
onClick={() => {
setSelectedGame(null)
setPopupMode(0)
setShowPopup(true)
setFadeOut(false)
}}
title='Click to download more games.'
>
Download game
</button>
</div>
<div
className={`main-container main-flex ${
platform() == 'windows'
? 'h-[calc(100vh-116px)]'
: 'h-[calc(100vh-84px)]'
}`}
>
{filteredGames.length == 0 ? (
<div className='flex justify-center items-center h-full'>
<p className='text-3xl'>No games installed</p>
</div>
) : (
filteredGames.map(i => (
<div
key={i.id}
className='main-entry'
title='Click to view game installs.'
onClick={() => {
setCategory(-1)
router.push('/main/game?id=' + i.id)
}}
onContextMenu={e => {
e.preventDefault()
setManagingGame(i.id)
setPopupMode(5)
setShowPopup(true)
setFadeOut(false)
}}
>
<div className='h-18 w-screen relative'>
{developerMode && (
<p className='absolute bottom-0 right-0 text-sm'>{i.id}</p>
)}
<p className='text-2xl'>{i.name}</p>
<div className='flex gap-2 absolute left-0 bottom-0'>
<div
className='entry-info-item'
title='The amount of versions installed of this game in installed/installable format.'
>
<p>
{(() => {
const gameVersions = (
serverVersionList?.versions ?? []
).filter(v => v.game === i.id)
const installed = gameVersions.filter(v =>
Object.keys(versionsList).includes(v.id)
).length
return gameVersions.length
? `${installed}/${gameVersions.length}`
: 'N/A'
})()}{' '}
versions installed
</p>
</div>
{i.official ? (
<div
className='entry-info-item'
title='This game is official.'
>
<FontAwesomeIcon icon={faCheck} color='#19c84b' />
<p>Official</p>
</div>
) : (
<div
className='entry-info-item'
title={
i.verified
? 'This game is verified to be safe'
: 'This game is not verified to be save. Proceed with caution.'
}
>
<FontAwesomeIcon
icon={i.verified ? faShieldHalved : faWarning}
color={i.verified ? '#19c84b' : '#ffc800'}
/>
<p>{i.verified ? 'Verified' : 'Unverified'}</p>
</div>
)}
</div>
</div>
</div>
))
)}
</div>
</div>
)
}
+2 -2
View File
@@ -354,7 +354,7 @@ export default function RootLayout ({
}
const closePopup = useCallback(() => {
if (popupMode == 0 && selectedGame && pathname === '/main') {
if (popupMode == 0 && selectedGame && pathname === '/main/games') {
setSelectedGame(null)
setSelectedVersionList([])
} else if (viewingInfoFromDownloads) {
@@ -938,7 +938,7 @@ export default function RootLayout ({
icon={
(popupMode == 0 &&
selectedGame &&
pathname === '/main') ||
pathname === '/main/games') ||
viewingInfoFromDownloads ||
(popupMode == 3 && showModInfo) ||
popupMode == 4
-150
View File
@@ -1,150 +0,0 @@
'use client'
import './Installs.css'
import { useEffect } from 'react'
import { useGlobal } from '@/providers/GlobalProvider'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import {
faCheck,
faShieldHalved,
faWarning
} from '@fortawesome/free-solid-svg-icons'
import { platform } from '@tauri-apps/plugin-os'
import { useRouter } from 'next/navigation'
export default function Installs () {
const {
versionsList,
serverVersionList,
showPopup,
setShowPopup,
setPopupMode,
setFadeOut,
setSelectedVersionList,
setSelectedGame,
setCategory,
setManagingGame,
developerMode
} = useGlobal()
const router = useRouter()
useEffect(() => {
if (!showPopup) return
setSelectedVersionList([])
}, [setSelectedVersionList, showPopup])
const filteredGames =
serverVersionList?.games.filter(g =>
serverVersionList.versions
.filter(v => v.game === g.id)
.some(v => Object.keys(versionsList).includes(v.id))
) ?? []
return (
<div className='mx-4 mt-4'>
<div className='flex justify-between items-center mb-4'>
<p className='text-3xl'>Games</p>
<button
className='button btntheme1'
onClick={() => {
setSelectedGame(null)
setPopupMode(0)
setShowPopup(true)
setFadeOut(false)
}}
title='Click to download more games.'
>
Download game
</button>
</div>
<div className='downloads-container'>
<div
className={`downloads-scroll ${
platform() == 'windows'
? 'h-[calc(100vh-116px)]'
: 'h-[calc(100vh-84px)]'
}`}
>
{filteredGames.length == 0 ? (
<div className='flex justify-center items-center h-full'>
<p className='text-3xl'>No games installed</p>
</div>
) : (
filteredGames.map(i => (
<div
key={i.id}
className='downloads-entry'
title='Click to view game installs.'
onClick={() => {
setCategory(-1)
router.push('/main/game?id=' + i.id)
}}
onContextMenu={e => {
e.preventDefault()
setManagingGame(i.id)
setPopupMode(5)
setShowPopup(true)
setFadeOut(false)
}}
>
<div className='h-18 w-screen relative'>
{developerMode && (
<p className='absolute bottom-0 right-0 text-sm'>{i.id}</p>
)}
<p className='text-2xl'>{i.name}</p>
<div className='flex gap-2 absolute left-0 bottom-0'>
<div
className='entry-info-item'
title='The amount of versions installed of this game in installed/installable format.'
>
<p>
{(() => {
const gameVersions = (
serverVersionList?.versions ?? []
).filter(v => v.game === i.id)
const installed = gameVersions.filter(v =>
Object.keys(versionsList).includes(v.id)
).length
return gameVersions.length
? `${installed}/${gameVersions.length}`
: 'N/A'
})()}{' '}
versions installed
</p>
</div>
{i.official ? (
<div
className='entry-info-item'
title='This game is official.'
>
<FontAwesomeIcon icon={faCheck} color='#19c84b' />
<p>Official</p>
</div>
) : (
<div
className='entry-info-item'
title={
i.verified
? 'This game is verified to be safe'
: 'This game is not verified to be save. Proceed with caution.'
}
>
<FontAwesomeIcon
icon={i.verified ? faShieldHalved : faWarning}
color={i.verified ? '#19c84b' : '#ffc800'}
/>
<p>{i.verified ? 'Verified' : 'Unverified'}</p>
</div>
)}
</div>
</div>
</div>
))
)}
</div>
</div>
</div>
)
}
+1 -1
View File
@@ -13,7 +13,7 @@ import { BaseDirectory, exists, readDir, remove } from '@tauri-apps/plugin-fs'
import { openPath } from '@tauri-apps/plugin-opener'
import { appLocalDataDir } from '@tauri-apps/api/path'
export default function Settings () {
export default function SettingsPage () {
const {
settings,
versions,
+13 -3
View File
@@ -9,6 +9,7 @@ import {
faDownload,
faGamepad,
faHexagonNodes,
faHome,
faLayerGroup,
faUser,
faUserShield
@@ -100,7 +101,16 @@ export default function Sidebar () {
draggable={false}
href='/main'
className={`link relative flex items-center ${
pathname === '/main' || pathname.startsWith('/main/game')
pathname === '/main' ? 'active' : ''
}`}
>
<FontAwesomeIcon icon={faHome} className='mr-2' /> Home
</Link>
<Link
draggable={false}
href='/main/games'
className={`link relative flex items-center ${
pathname === '/main/games' || pathname.startsWith('/main/game')
? 'active'
: ''
}`}
@@ -147,7 +157,7 @@ export default function Sidebar () {
: ''
} ml-auto w-50 ${
sidebarAlwaysShowGames ||
pathname === '/main' ||
pathname === '/main/games' ||
pathname.startsWith('/main/game')
? ''
: 'hidden'
@@ -206,7 +216,7 @@ export default function Sidebar () {
: ''
} ml-auto w-47.5 ${
sidebarAlwaysShowGames ||
pathname === '/main' ||
pathname === '/main/games' ||
pathname.startsWith('/main/game')
? ''
: 'hidden'
+16 -4
View File
@@ -70,14 +70,14 @@ body {
}
.btntheme2,
.downloads-entry .entry-info-item,
.downloads-entry .button {
.main-container .main-entry .entry-info-item,
.main-container .main-entry .button {
@apply bg-(--col3) border-(--col5) hover:bg-(--col5) hover:border-(--col7) disabled:text-gray-300 disabled:bg-(--col2) disabled:border-(--col6) disabled:hover:text-gray-100 disabled:hover:bg-(--col3) disabled:hover:border-(--col7);
}
.btntheme3,
.downloads-entry:hover .entry-info-item,
.downloads-entry:hover .button {
.main-container .main-entry:hover .entry-info-item,
.main-container .main-entry:hover .button {
@apply bg-(--col4) border-(--col6) hover:bg-(--col6) hover:border-(--col8) disabled:text-gray-300 disabled:bg-(--col2) disabled:border-(--col7) disabled:hover:text-gray-100 disabled:hover:bg-(--col4) disabled:hover:border-(--col8);
}
@@ -123,6 +123,18 @@ body {
}
}
.main-container {
@apply bg-(--col1) border border-(--col3) rounded-lg overflow-y-auto w-full;
}
.main-container.main-flex {
@apply flex flex-col gap-2 p-2;
}
.main-container .main-entry {
@apply flex justify-between items-center p-2 rounded-lg text-gray-200 text-lg transition-colors bg-(--col2) hover:bg-(--col3) border border-(--col4) hover:border-(--col5) cursor-pointer;
}
.popup-overlay {
@apply fixed w-screen h-screen z-99999 flex justify-center items-center animate-[fadeIn_0.2s_ease-out_forwards] left-0 top-0 bg-[rgba(0,0,0,0.5)];
}