forked from Berry-Dash/launcher
Improvements again
This commit is contained in:
17
src/componets/ProgressBar.tsx
Normal file
17
src/componets/ProgressBar.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
type ProgressBarProps = {
|
||||
progress: number
|
||||
className?: string
|
||||
}
|
||||
|
||||
export default function ProgressBar ({ progress, className }: ProgressBarProps) {
|
||||
return (
|
||||
<div
|
||||
className={`w-full bg-(--col1) border border-(--col5) rounded-full h-4 overflow-hidden ${className}`}
|
||||
>
|
||||
<div
|
||||
className='bg-(--col8) border-r border-r-(--col5) h-full'
|
||||
style={{ width: `${progress}%` }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
17
src/componets/Setting.css
Normal file
17
src/componets/Setting.css
Normal file
@@ -0,0 +1,17 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
.setting-checkbox-wrapper {
|
||||
@apply relative w-5 h-5;
|
||||
}
|
||||
|
||||
.setting-checkbox {
|
||||
@apply appearance-none w-full h-full border-2 border-(--col4) rounded-md bg-(--col2) transition-colors cursor-pointer;
|
||||
}
|
||||
|
||||
.setting-checkbox:checked {
|
||||
@apply bg-(--col4) border-(--col6);
|
||||
}
|
||||
|
||||
.fa-check-icon {
|
||||
@apply absolute top-1/2 left-1/2 text-white text-[11px] pointer-events-none -translate-x-2/4 -translate-y-2/4;
|
||||
}
|
||||
27
src/componets/Setting.tsx
Normal file
27
src/componets/Setting.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import './Setting.css'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faCheck } from '@fortawesome/free-solid-svg-icons'
|
||||
import { SettingProps } from '@/types/SettingProps'
|
||||
|
||||
export function Setting ({
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
className,
|
||||
title
|
||||
}: SettingProps) {
|
||||
return (
|
||||
<div className={`flex items-center gap-2 mb-2 ${className}`} title={title}>
|
||||
<label className='text-white text-lg'>{label}</label>
|
||||
<div className='setting-checkbox-wrapper'>
|
||||
<input
|
||||
type='checkbox'
|
||||
className='setting-checkbox'
|
||||
checked={value}
|
||||
onChange={() => onChange(!value)}
|
||||
/>
|
||||
{value && <FontAwesomeIcon icon={faCheck} className='fa-check-icon' />}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
47
src/componets/Sidebar.css
Normal file
47
src/componets/Sidebar.css
Normal file
@@ -0,0 +1,47 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
.sidebar {
|
||||
@apply fixed top-0 left-0 w-60 h-screen bg-(--col1) flex flex-col border-e border-(--col2) z-1;
|
||||
}
|
||||
|
||||
.sidebar-downloads {
|
||||
@apply text-[#bdbdbd] fixed bottom-2 left-2 bg-(--col2) rounded-lg border border-(--col3) px-4 py-3 text-xl cursor-pointer transition-colors duration-[0.25s] opacity-75;
|
||||
}
|
||||
|
||||
.sidebar-downloads:hover {
|
||||
@apply text-white opacity-100;
|
||||
@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;
|
||||
}
|
||||
|
||||
.link {
|
||||
@apply text-[#bdbdbd] p-2 rounded-md no-underline cursor-pointer transition-colors duration-[0.25s] border border-transparent;
|
||||
}
|
||||
|
||||
.link.active {
|
||||
@apply bg-(--col2) border-(--col3);
|
||||
}
|
||||
|
||||
.link.active,
|
||||
.link:hover {
|
||||
@apply text-white;
|
||||
}
|
||||
|
||||
.link.active:hover {
|
||||
@apply bg-(--col3) border-(--col4);
|
||||
}
|
||||
|
||||
.sidebar ::-webkit-scrollbar {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.macos-drag {
|
||||
@apply h-7 fixed top-0 left-0 w-60 -z-10;
|
||||
}
|
||||
213
src/componets/Sidebar.tsx
Normal file
213
src/componets/Sidebar.tsx
Normal file
@@ -0,0 +1,213 @@
|
||||
'use client'
|
||||
|
||||
import './Sidebar.css'
|
||||
import Icon from '@/assets/Icon.png'
|
||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import {
|
||||
faCog,
|
||||
faDownload,
|
||||
faGamepad,
|
||||
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 '@/app/GlobalProvider'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
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']
|
||||
})
|
||||
|
||||
export default function Sidebar () {
|
||||
const {
|
||||
normalConfig,
|
||||
getListOfGames,
|
||||
setShowPopup,
|
||||
setPopupMode,
|
||||
setFadeOut,
|
||||
downloadProgress,
|
||||
downloadedVersionsConfig,
|
||||
getVersionInfo,
|
||||
category,
|
||||
setCategory
|
||||
} = useGlobal()
|
||||
|
||||
const pathname = usePathname()
|
||||
const params = useSearchParams()
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<aside className='sidebar'>
|
||||
<div
|
||||
className='macos-drag'
|
||||
hidden={platform() != 'macos'}
|
||||
onMouseDown={e => {
|
||||
if (e.buttons === 1) {
|
||||
if (e.detail === 2) {
|
||||
getCurrentWindow().toggleMaximize()
|
||||
} else {
|
||||
getCurrentWindow().startDragging()
|
||||
}
|
||||
}
|
||||
}}
|
||||
></div>
|
||||
<div
|
||||
className='logo'
|
||||
style={{
|
||||
marginTop:
|
||||
platform() == 'windows'
|
||||
? '32px'
|
||||
: platform() == 'macos'
|
||||
? '28px'
|
||||
: ''
|
||||
}}
|
||||
onMouseDown={e => {
|
||||
if (platform() != 'macos') return
|
||||
if (e.buttons === 1) {
|
||||
if (e.detail === 2) {
|
||||
getCurrentWindow().toggleMaximize()
|
||||
} else {
|
||||
getCurrentWindow().startDragging()
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Image draggable={false} src={Icon} width={36} height={36} alt='' />
|
||||
<p className={`ml-1 text-[16px] whitespace-nowrap ${lexend.className}`}>
|
||||
Lncvrt Games Launcher
|
||||
</p>
|
||||
</div>
|
||||
<nav className='nav-links overflow-auto pt-2'>
|
||||
<Link
|
||||
draggable={false}
|
||||
href='/'
|
||||
className={`link relative flex items-center ${
|
||||
pathname === '/' || pathname === '/game' ? 'active' : ''
|
||||
}`}
|
||||
>
|
||||
<FontAwesomeIcon icon={faHexagonNodes} className='mr-2' /> Games
|
||||
</Link>
|
||||
{getListOfGames()
|
||||
.sort((a, b) => {
|
||||
return a.id - b.id
|
||||
})
|
||||
.map(i => (
|
||||
<React.Fragment key={i.id}>
|
||||
<div
|
||||
draggable={false}
|
||||
className={`link ${
|
||||
pathname === '/game' && Number(params.get('id') || 0) == i.id
|
||||
? 'active'
|
||||
: ''
|
||||
} ml-auto w-50 ${
|
||||
normalConfig?.settings.alwaysShowGamesInSidebar ||
|
||||
pathname === '/' ||
|
||||
pathname === '/game'
|
||||
? ''
|
||||
: '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'>{value}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
<Link
|
||||
draggable={false}
|
||||
href='/settings'
|
||||
className={`link ${pathname === '/settings' ? 'active' : ''}`}
|
||||
>
|
||||
<FontAwesomeIcon icon={faCog} className='mr-1' /> Settings
|
||||
</Link>
|
||||
<button
|
||||
onClick={() => openUrl('https://games.lncvrt.xyz/discord')}
|
||||
className='link mr-auto'
|
||||
>
|
||||
<FontAwesomeIcon icon={faDiscord} className='mr-1' /> Community
|
||||
</button>
|
||||
</nav>
|
||||
<div
|
||||
className='sidebar-downloads'
|
||||
hidden={downloadProgress.length == 0}
|
||||
onClick={() => {
|
||||
setPopupMode(1)
|
||||
setShowPopup(true)
|
||||
setFadeOut(false)
|
||||
}}
|
||||
>
|
||||
<p>
|
||||
<FontAwesomeIcon icon={faDownload} /> Downloads
|
||||
</p>
|
||||
</div>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
131
src/componets/popups/VersionInfo.tsx
Normal file
131
src/componets/popups/VersionInfo.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
faArrowUpRightFromSquare,
|
||||
faCheck,
|
||||
faCode,
|
||||
faHardDrive,
|
||||
faShieldHalved,
|
||||
faWarning
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { useGlobal } from '@/app/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 {
|
||||
getGameInfo,
|
||||
getVersionInfo,
|
||||
managingVersion,
|
||||
downloadedVersionsConfig,
|
||||
viewingInfoFromDownloads
|
||||
} = useGlobal()
|
||||
const [versionSize, setVersionSize] = useState<number>(0)
|
||||
|
||||
useEffect(() => {
|
||||
invoke<string>('folder_size', {
|
||||
version: managingVersion
|
||||
}).then(size => {
|
||||
setVersionSize(Number(size))
|
||||
})
|
||||
}, [managingVersion, setVersionSize])
|
||||
|
||||
if (!managingVersion || !downloadedVersionsConfig) return <></>
|
||||
|
||||
const versionInfo = getVersionInfo(managingVersion)
|
||||
const gameInfo = getGameInfo(versionInfo?.game)
|
||||
|
||||
return (
|
||||
<>
|
||||
<p className='text-xl text-center'>
|
||||
Viewing info for {versionInfo?.displayName}
|
||||
</p>
|
||||
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
|
||||
<div
|
||||
className='entry-info-item btntheme2'
|
||||
hidden={viewingInfoFromDownloads}
|
||||
>
|
||||
<p>
|
||||
Installed{' '}
|
||||
{new Intl.DateTimeFormat(undefined).format(
|
||||
downloadedVersionsConfig.list[managingVersion] ?? 0
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className='entry-info-item btntheme2'
|
||||
hidden={!versionInfo || versionInfo.releaseDate == 0}
|
||||
>
|
||||
<p>
|
||||
Released{' '}
|
||||
{new Intl.DateTimeFormat(undefined).format(
|
||||
versionInfo?.releaseDate ? versionInfo.releaseDate * 1000 : 0
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className='entry-info-item btntheme2' hidden={!gameInfo?.official}>
|
||||
<FontAwesomeIcon icon={faCheck} color='#19c84b' />
|
||||
<p>Official</p>
|
||||
</div>
|
||||
<div className='entry-info-item btntheme2' hidden={gameInfo?.official}>
|
||||
<FontAwesomeIcon
|
||||
icon={gameInfo?.verified ? faShieldHalved : faWarning}
|
||||
color={gameInfo?.verified ? '#19c84b' : '#ffc800'}
|
||||
/>
|
||||
<p>{gameInfo?.verified ? 'Verified' : 'Unverified'}</p>
|
||||
</div>
|
||||
<div
|
||||
className='entry-info-item btntheme2'
|
||||
hidden={gameInfo?.developer == null}
|
||||
>
|
||||
<FontAwesomeIcon icon={faCode} color='lightgray' />
|
||||
<p>Developer: {gameInfo?.developer}</p>
|
||||
</div>
|
||||
<div
|
||||
className='entry-info-item btntheme2'
|
||||
hidden={viewingInfoFromDownloads || versionSize === null}
|
||||
>
|
||||
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
|
||||
<p>
|
||||
Size on disk:{' '}
|
||||
{versionSize > 0
|
||||
? prettyBytes(versionSize, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})
|
||||
: 'N/A'}
|
||||
</p>
|
||||
</div>
|
||||
<div className='entry-info-item btntheme2' hidden={!versionInfo}>
|
||||
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
|
||||
<p>
|
||||
Size when downloaded (zipped):{' '}
|
||||
{versionInfo
|
||||
? prettyBytes(versionInfo.size, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})
|
||||
: 'Loading...'}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className='entry-info-item btntheme2'
|
||||
onClick={async () => {
|
||||
if (!versionInfo) return
|
||||
await message(atob(versionInfo.changelog), {
|
||||
title: 'Changelog for ' + versionInfo.displayName,
|
||||
kind: 'info'
|
||||
})
|
||||
}}
|
||||
hidden={!versionInfo?.changelog}
|
||||
>
|
||||
<p>View Changelog</p>
|
||||
<FontAwesomeIcon icon={faArrowUpRightFromSquare} color='lightgray' />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user