Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
654405e016
|
||
|
|
c461a0a459
|
||
|
|
16ce04ba7a
|
||
|
|
1700d127c3
|
||
|
|
7c47b32c68
|
||
|
|
56781b5d1a
|
||
|
|
6ce7c2a8ae
|
||
|
|
15dd9ad013
|
||
|
|
217222a22b
|
||
|
|
39da88a856
|
||
|
|
29c90d2f06
|
||
|
|
467dd1b0a1
|
||
|
|
a1a69c9ac5
|
||
|
|
0163e09b02
|
||
|
|
fbb4b15971
|
||
|
|
77933a2298
|
||
|
|
7209a36752
|
||
|
|
f00b78996a
|
||
|
|
38779c1715
|
||
|
|
fa67d26fe9
|
||
|
|
81d47ea71d
|
||
|
|
70433b76ab
|
||
|
|
105913503c
|
||
|
|
2ad54f976b
|
||
|
|
d8d59febbb
|
||
|
|
a618c76292
|
||
|
|
118fad49d8
|
No files matched your search
@@ -29,7 +29,7 @@
|
|||||||
"@tauri-apps/cli": "2.10.0",
|
"@tauri-apps/cli": "2.10.0",
|
||||||
"@types/crypto-js": "4.2.2",
|
"@types/crypto-js": "4.2.2",
|
||||||
"@types/node": "25.2.3",
|
"@types/node": "25.2.3",
|
||||||
"@types/react": "19.2.13",
|
"@types/react": "19.2.14",
|
||||||
"@types/react-dom": "19.2.3",
|
"@types/react-dom": "19.2.3",
|
||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
"eslint": "9.39.2",
|
"eslint": "9.39.2",
|
||||||
@@ -292,7 +292,7 @@
|
|||||||
|
|
||||||
"@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="],
|
"@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=="],
|
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
|
||||||
|
|
||||||
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
|
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "lncvrt-games-launcher",
|
"name": "lncvrt-games-launcher",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.5.8",
|
"version": "1.6.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
"@tauri-apps/cli": "2.10.0",
|
"@tauri-apps/cli": "2.10.0",
|
||||||
"@types/crypto-js": "4.2.2",
|
"@types/crypto-js": "4.2.2",
|
||||||
"@types/node": "25.2.3",
|
"@types/node": "25.2.3",
|
||||||
"@types/react": "19.2.13",
|
"@types/react": "19.2.14",
|
||||||
"@types/react-dom": "19.2.3",
|
"@types/react-dom": "19.2.3",
|
||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
"eslint": "9.39.2",
|
"eslint": "9.39.2",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lncvrt-games-launcher"
|
name = "lncvrt-games-launcher"
|
||||||
version = "1.5.8"
|
version = "1.6.1"
|
||||||
authors = ["Lncvrt"]
|
authors = ["Lncvrt"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
@@ -22,13 +22,14 @@ tokio = "1.49.0"
|
|||||||
futures-util = { version = "0.3.31", features = ["io"] }
|
futures-util = { version = "0.3.31", features = ["io"] }
|
||||||
tauri-plugin-decorum = "1.1.1"
|
tauri-plugin-decorum = "1.1.1"
|
||||||
tauri-plugin-fs = "2.4.5"
|
tauri-plugin-fs = "2.4.5"
|
||||||
zip = "7.4.0"
|
zip = "8.0.0"
|
||||||
libc = "0.2.181"
|
libc = "0.2.182"
|
||||||
tauri-plugin-dialog = "2.6.0"
|
tauri-plugin-dialog = "2.6.0"
|
||||||
tauri-plugin-notification = "2.3.3"
|
tauri-plugin-notification = "2.3.3"
|
||||||
sysinfo = "0.38.1"
|
sysinfo = "0.38.1"
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
tauri-plugin-clipboard-manager = "2.3.2"
|
tauri-plugin-clipboard-manager = "2.3.2"
|
||||||
|
tauri-plugin-prevent-default = "4.0.3"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
shlex = "1.3.0"
|
shlex = "1.3.0"
|
||||||
|
|||||||
@@ -32,6 +32,12 @@
|
|||||||
"allow": [
|
"allow": [
|
||||||
{ "path": "$APPLOCALDATA/game/**" }
|
{ "path": "$APPLOCALDATA/game/**" }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "opener:allow-open-path",
|
||||||
|
"allow": [
|
||||||
|
{ "path": "$APPLOCALDATA/game/**" }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
+21
-32
@@ -12,9 +12,8 @@ use std::{
|
|||||||
};
|
};
|
||||||
use sysinfo::System;
|
use sysinfo::System;
|
||||||
use tauri::{AppHandle, Emitter, Manager};
|
use tauri::{AppHandle, Emitter, Manager};
|
||||||
use tauri_plugin_dialog::{DialogExt, MessageDialogKind};
|
|
||||||
use tauri_plugin_opener::OpenerExt;
|
|
||||||
use tauri_plugin_os::platform;
|
use tauri_plugin_os::platform;
|
||||||
|
use tauri_plugin_prevent_default::Flags;
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
use tokio::{io::AsyncWriteExt, time::timeout};
|
use tokio::{io::AsyncWriteExt, time::timeout};
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
@@ -280,6 +279,9 @@ fn launch_game(
|
|||||||
//if already running on macos, it'll auto take the user to that proccess
|
//if already running on macos, it'll auto take the user to that proccess
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
#[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) {
|
if !use_wine && is_running_by_path(&exe_path) {
|
||||||
app.dialog()
|
app.dialog()
|
||||||
.message(format!(
|
.message(format!(
|
||||||
@@ -324,34 +326,26 @@ fn launch_game(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn open_folder(app: AppHandle, name: String) {
|
|
||||||
let game_path = app
|
|
||||||
.path()
|
|
||||||
.app_local_data_dir()
|
|
||||||
.unwrap()
|
|
||||||
.join("game")
|
|
||||||
.join(&name);
|
|
||||||
if game_path.exists() {
|
|
||||||
app.opener()
|
|
||||||
.open_path(game_path.to_string_lossy(), None::<&str>)
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
app.dialog()
|
|
||||||
.message(format!(
|
|
||||||
"Game folder \"{}\" not found.",
|
|
||||||
game_path.display()
|
|
||||||
))
|
|
||||||
.kind(MessageDialogKind::Error)
|
|
||||||
.title("Folder not found")
|
|
||||||
.show(|_| {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
tauri::Builder::default()
|
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_window_state::Builder::new().build())
|
||||||
.plugin(tauri_plugin_clipboard_manager::init())
|
.plugin(tauri_plugin_clipboard_manager::init())
|
||||||
.plugin(tauri_plugin_notification::init())
|
.plugin(tauri_plugin_notification::init())
|
||||||
@@ -366,12 +360,7 @@ pub fn run() {
|
|||||||
.plugin(tauri_plugin_decorum::init())
|
.plugin(tauri_plugin_decorum::init())
|
||||||
.plugin(tauri_plugin_os::init())
|
.plugin(tauri_plugin_os::init())
|
||||||
.plugin(tauri_plugin_opener::init())
|
.plugin(tauri_plugin_opener::init())
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![download, launch_game, folder_size])
|
||||||
download,
|
|
||||||
launch_game,
|
|
||||||
open_folder,
|
|
||||||
folder_size
|
|
||||||
])
|
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Lncvrt Games Launcher",
|
"productName": "Lncvrt Games Launcher",
|
||||||
"version": "1.5.8",
|
"version": "1.6.1",
|
||||||
"identifier": "xyz.lncvrt.games-launcher",
|
"identifier": "xyz.lncvrt.games-launcher",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "next dev",
|
"beforeDevCommand": "next dev --webpack",
|
||||||
"devUrl": "http://localhost:3000",
|
"devUrl": "http://localhost:3000",
|
||||||
"beforeBuildCommand": "next build",
|
"beforeBuildCommand": "next build",
|
||||||
"frontendDist": "../out"
|
"frontendDist": "../out"
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Lncvrt Games Launcher",
|
"productName": "Lncvrt Games Launcher",
|
||||||
"version": "1.5.8",
|
"version": "1.6.1",
|
||||||
"identifier": "xyz.lncvrt.games-launcher",
|
"identifier": "xyz.lncvrt.games-launcher",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "next dev",
|
"beforeDevCommand": "next dev --webpack",
|
||||||
"devUrl": "http://localhost:3000",
|
"devUrl": "http://localhost:3000",
|
||||||
"beforeBuildCommand": "next build",
|
"beforeBuildCommand": "next build",
|
||||||
"frontendDist": "../out"
|
"frontendDist": "../out"
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Lncvrt Games Launcher",
|
"productName": "Lncvrt Games Launcher",
|
||||||
"version": "1.5.8",
|
"version": "1.6.1",
|
||||||
"identifier": "xyz.lncvrt.games-launcher",
|
"identifier": "xyz.lncvrt.games-launcher",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "next dev",
|
"beforeDevCommand": "next dev --webpack",
|
||||||
"devUrl": "http://localhost:3000",
|
"devUrl": "http://localhost:3000",
|
||||||
"beforeBuildCommand": "next build",
|
"beforeBuildCommand": "next build",
|
||||||
"frontendDist": "../out"
|
"frontendDist": "../out"
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"active": true,
|
"active": true,
|
||||||
"targets": ["app"],
|
"targets": ["app"],
|
||||||
"macOS": {
|
"macOS": {
|
||||||
"minimumSystemVersion": "15.0"
|
"minimumSystemVersion": "13.0"
|
||||||
},
|
},
|
||||||
"icon": ["icons/icon.icns"]
|
"icon": ["icons/icon.icns"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Lncvrt Games Launcher",
|
"productName": "Lncvrt Games Launcher",
|
||||||
"version": "1.5.8",
|
"version": "1.6.1",
|
||||||
"identifier": "xyz.lncvrt.games-launcher",
|
"identifier": "xyz.lncvrt.games-launcher",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "next dev",
|
"beforeDevCommand": "next dev --webpack",
|
||||||
"devUrl": "http://localhost:3000",
|
"devUrl": "http://localhost:3000",
|
||||||
"beforeBuildCommand": "next build",
|
"beforeBuildCommand": "next build",
|
||||||
"frontendDist": "../out"
|
"frontendDist": "../out"
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import {
|
|||||||
Dispatch,
|
Dispatch,
|
||||||
SetStateAction
|
SetStateAction
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { DownloadProgress } from './types/DownloadProgress'
|
import { DownloadProgress } from '@/types/DownloadProgress'
|
||||||
import { VersionsConfig } from './types/VersionsConfig'
|
import { VersionsConfig } from '@/types/VersionsConfig'
|
||||||
import { NormalConfig } from './types/NormalConfig'
|
import { NormalConfig } from '@/types/NormalConfig'
|
||||||
import { ServerVersionsResponse } from './types/ServerVersionsResponse'
|
import { ServerVersionsResponse } from '@/types/ServerVersionsResponse'
|
||||||
import { GameVersion } from './types/GameVersion'
|
import { GameVersion } from '@/types/GameVersion'
|
||||||
import { Game } from './types/Game'
|
import { Game } from '@/types/Game'
|
||||||
|
|
||||||
type GlobalCtxType = {
|
type GlobalCtxType = {
|
||||||
serverVersionList: ServerVersionsResponse | null
|
serverVersionList: ServerVersionsResponse | null
|
||||||
@@ -45,6 +45,12 @@ type GlobalCtxType = {
|
|||||||
downloadVersions: (list: string[]) => Promise<void>
|
downloadVersions: (list: string[]) => Promise<void>
|
||||||
category: number
|
category: number
|
||||||
setCategory: Dispatch<SetStateAction<number>>
|
setCategory: Dispatch<SetStateAction<number>>
|
||||||
|
downloadQueue: string[]
|
||||||
|
setDownloadQueue: Dispatch<SetStateAction<string[]>>
|
||||||
|
closePopup: () => void
|
||||||
|
getSpecialVersionsList(game?: number | undefined): GameVersion[]
|
||||||
|
selectedGame: number | null
|
||||||
|
setViewingInfoFromDownloads: Dispatch<SetStateAction<boolean>>
|
||||||
}
|
}
|
||||||
|
|
||||||
const GlobalCtx = createContext<GlobalCtxType | null>(null)
|
const GlobalCtx = createContext<GlobalCtxType | null>(null)
|
||||||
|
|||||||
@@ -9,5 +9,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.downloads-entry {
|
.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);
|
@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;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,323 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
|
import { BirdColor } from '@/types/BerryDash/BirdColor'
|
||||||
|
import axios from 'axios'
|
||||||
|
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
|
||||||
|
username: string
|
||||||
|
value: number
|
||||||
|
icon: number
|
||||||
|
overlay: number
|
||||||
|
birdColor: BirdColor
|
||||||
|
overlayColor: BirdColor
|
||||||
|
customIcon: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Stats {
|
||||||
|
highScore: string
|
||||||
|
totalNormalBerries: string
|
||||||
|
totalPoisonBerries: string
|
||||||
|
totalSlowBerries: string
|
||||||
|
totalUltraBerries: string
|
||||||
|
totalSpeedyBerries: string
|
||||||
|
totalCoinBerries: string
|
||||||
|
totalRandomBerries: string
|
||||||
|
totalAntiBerries: string
|
||||||
|
totalGoldenBerries: string
|
||||||
|
coins: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LeaderboardEntry extends BaseEntry {
|
||||||
|
type: 'leaderboard'
|
||||||
|
value: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Account extends BaseEntry {
|
||||||
|
type: 'account'
|
||||||
|
stats: Stats
|
||||||
|
xp: bigint
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateXP (
|
||||||
|
normalBerries: bigint,
|
||||||
|
poisonBerries: bigint,
|
||||||
|
slowBerries: bigint,
|
||||||
|
ultraBerries: bigint,
|
||||||
|
speedyBerries: bigint,
|
||||||
|
coinBerries: bigint,
|
||||||
|
randomBerries: bigint,
|
||||||
|
antiBerries: bigint,
|
||||||
|
goldenBerries: bigint
|
||||||
|
): bigint {
|
||||||
|
let totalXp = 0n
|
||||||
|
totalXp += normalBerries
|
||||||
|
totalXp -= poisonBerries
|
||||||
|
totalXp -= slowBerries
|
||||||
|
totalXp += ultraBerries * 5n
|
||||||
|
totalXp += speedyBerries * 10n
|
||||||
|
totalXp += coinBerries * 10n
|
||||||
|
totalXp += randomBerries
|
||||||
|
totalXp -= antiBerries
|
||||||
|
totalXp += goldenBerries * 4n
|
||||||
|
|
||||||
|
if (totalXp < 0n) totalXp = 0n
|
||||||
|
return totalXp
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateLevel (xp: bigint): number {
|
||||||
|
const levelDivisor = 50.0
|
||||||
|
|
||||||
|
const xpNumber = Number(xp)
|
||||||
|
const discriminant = 95 * 95 + levelDivisor * 2 * xpNumber
|
||||||
|
const level = (-95 + Math.sqrt(discriminant)) / levelDivisor
|
||||||
|
return Math.floor(level) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BerryDashLeaderboards () {
|
||||||
|
const [selected, setSelected] = useState<number>(-1)
|
||||||
|
const [selectedBerryOption, setSelectedBerryOption] = useState<number>(0)
|
||||||
|
const [entries, setEntries] = useState<(LeaderboardEntry | Account)[]>([])
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const Refresh = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
if (selected == 3 || selected == 4) {
|
||||||
|
const result = await axios.get(
|
||||||
|
'https://games.lncvrt.xyz/api/berrydash/account?username='
|
||||||
|
)
|
||||||
|
if (result.data.success) {
|
||||||
|
let accounts = result.data.data as Account[]
|
||||||
|
|
||||||
|
accounts = accounts.map(acc => {
|
||||||
|
const xp = calculateXP(
|
||||||
|
BigInt(acc.stats.totalNormalBerries),
|
||||||
|
BigInt(acc.stats.totalPoisonBerries),
|
||||||
|
BigInt(acc.stats.totalSlowBerries),
|
||||||
|
BigInt(acc.stats.totalUltraBerries),
|
||||||
|
BigInt(acc.stats.totalSpeedyBerries),
|
||||||
|
BigInt(acc.stats.totalCoinBerries),
|
||||||
|
BigInt(acc.stats.totalRandomBerries),
|
||||||
|
BigInt(acc.stats.totalAntiBerries),
|
||||||
|
BigInt(acc.stats.totalGoldenBerries)
|
||||||
|
)
|
||||||
|
return { ...acc, xp }
|
||||||
|
})
|
||||||
|
|
||||||
|
accounts.sort((a, b) => (b.xp > a.xp ? 1 : b.xp < a.xp ? -1 : 0))
|
||||||
|
|
||||||
|
setEntries(accounts)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const result = await axios.get(
|
||||||
|
'https://games.lncvrt.xyz/api/berrydash/leaderboard/' +
|
||||||
|
(selected == 0
|
||||||
|
? 'score'
|
||||||
|
: selected == 1
|
||||||
|
? 'berry?berry=' + selectedBerryOption
|
||||||
|
: selected == 2
|
||||||
|
? 'coin'
|
||||||
|
: selected == 5
|
||||||
|
? 'legacy'
|
||||||
|
: 'total')
|
||||||
|
)
|
||||||
|
if (result.data.success) {
|
||||||
|
setEntries(result.data.data as LeaderboardEntry[])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
setEntries([])
|
||||||
|
}
|
||||||
|
}, [selected, selectedBerryOption])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = 'Lncvrt Games - Berry Dash Leaderboards'
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selected != -1) setTimeout(() => Refresh(), 0)
|
||||||
|
}, [selected, Refresh])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='mx-4 mt-4'>
|
||||||
|
<div className='flex justify-between items-center mb-4'>
|
||||||
|
<p className='text-3xl'>Berry Dash Leaderboards</p>
|
||||||
|
<div className='flex gap-2'>
|
||||||
|
<button
|
||||||
|
className='button btntheme1'
|
||||||
|
onClick={() => {
|
||||||
|
setEntries([])
|
||||||
|
Refresh()
|
||||||
|
}}
|
||||||
|
title='Click to refresh the leaderboards.'
|
||||||
|
hidden={selected == -1}
|
||||||
|
>
|
||||||
|
Refresh
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='button btntheme1'
|
||||||
|
onClick={() => {
|
||||||
|
if (selected == -1) router.push('/game?id=1')
|
||||||
|
else {
|
||||||
|
setSelected(-1)
|
||||||
|
setEntries([])
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
title='Click to go up a level.'
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`box ${
|
||||||
|
platform() == 'windows'
|
||||||
|
? 'h-[calc(100vh-116px)]'
|
||||||
|
: 'h-[calc(100vh-84px)]'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{selected == -1 ? (
|
||||||
|
<>
|
||||||
|
<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)}
|
||||||
|
>
|
||||||
|
Score Leaderboard
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='leaderboard-button'
|
||||||
|
onClick={() => setSelected(1)}
|
||||||
|
>
|
||||||
|
Berry Leaderboard
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='leaderboard-button'
|
||||||
|
onClick={() => setSelected(2)}
|
||||||
|
>
|
||||||
|
Coins Leaderboard
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='leaderboard-button'
|
||||||
|
onClick={() => setSelected(3)}
|
||||||
|
>
|
||||||
|
Level Leaderboard
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='leaderboard-button'
|
||||||
|
onClick={() => setSelected(4)}
|
||||||
|
>
|
||||||
|
Total XP Leaderboard
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='leaderboard-button'
|
||||||
|
onClick={() => setSelected(5)}
|
||||||
|
>
|
||||||
|
Legacy Leaderboard
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='leaderboard-button'
|
||||||
|
onClick={() => setSelected(6)}
|
||||||
|
>
|
||||||
|
Total Berries Leaderboard
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className={`flex flex-col gap-2 overflow-y-auto ${
|
||||||
|
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) => {
|
||||||
|
const isAccount = 'stats' in item
|
||||||
|
const isLeaderboard = 'value' in item && !isAccount
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={item.id}
|
||||||
|
className='leaderboard-entry flex justify-between items-center'
|
||||||
|
>
|
||||||
|
<div className='flex items-center gap-1'>
|
||||||
|
<Image
|
||||||
|
src={
|
||||||
|
!item.customIcon
|
||||||
|
? `https://games-r2.lncvrt.xyz/game-assets/berrydash/icons/bird_${
|
||||||
|
item.icon === 1
|
||||||
|
? GetIconForUser(item.id)
|
||||||
|
: item.icon
|
||||||
|
}.png`
|
||||||
|
: `https://games.lncvrt.xyz/api/berrydash/icon-marketplace/icon?id=${item.customIcon}&raw=true`
|
||||||
|
}
|
||||||
|
className='pointer-events-none'
|
||||||
|
width={48}
|
||||||
|
height={48}
|
||||||
|
alt=''
|
||||||
|
unoptimized
|
||||||
|
/>
|
||||||
|
<p>
|
||||||
|
{item.username} (#{index + 1})
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isLeaderboard ? (
|
||||||
|
<p>
|
||||||
|
{selected === 1 || selected === 6
|
||||||
|
? 'Berries'
|
||||||
|
: selected === 2
|
||||||
|
? 'Coins'
|
||||||
|
: 'Score'}
|
||||||
|
: {item.value.toLocaleString('en-US')}
|
||||||
|
</p>
|
||||||
|
) : isAccount ? (
|
||||||
|
<p>
|
||||||
|
{selected === 3 ? 'Level' : 'XP'}:{' '}
|
||||||
|
{selected === 3
|
||||||
|
? calculateLevel(item.xp).toLocaleString('en-US')
|
||||||
|
: item.xp.toLocaleString('en-US')}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{selected == 1 && (
|
||||||
|
<div className='flex justify-center'>
|
||||||
|
<select
|
||||||
|
value={selectedBerryOption}
|
||||||
|
onChange={e => setSelectedBerryOption(Number(e.target.value))}
|
||||||
|
className='leaderboard-select mt-0.75'
|
||||||
|
>
|
||||||
|
<option value='0'>Normal Berry</option>
|
||||||
|
<option value='1'>Poison Berry</option>
|
||||||
|
<option value='2'>Slow Berry</option>
|
||||||
|
<option value='3'>Ultra Berry</option>
|
||||||
|
<option value='4'>Speedy Berry</option>
|
||||||
|
<option value='5'>Coin Berry</option>
|
||||||
|
<option value='6'>Random Berry</option>
|
||||||
|
<option value='7'>Anti Berry</option>
|
||||||
|
<option value='8'>Golden Berry</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
.box {
|
||||||
|
@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 cursor-pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaderboard-entry {
|
||||||
|
@apply rounded-lg text-gray-200 text-lg px-1 py-0.5 transition-colors bg-(--col2) hover:bg-(--col3) border border-(--col4) hover:border-(--col5) cursor-pointer;
|
||||||
|
}
|
||||||
+94
-214
@@ -1,16 +1,17 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import '../Installs.css'
|
import '@/app/Installs.css'
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
import { useGlobal } from '../GlobalProvider'
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
import { useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { platform } from '@tauri-apps/plugin-os'
|
import { platform } from '@tauri-apps/plugin-os'
|
||||||
import { faWarning } from '@fortawesome/free-solid-svg-icons'
|
import { faWarning } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { ask } from '@tauri-apps/plugin-dialog'
|
import { ask } from '@tauri-apps/plugin-dialog'
|
||||||
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
|
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
|
||||||
import { writeVersionsConfig } from '../util/BazookaManager'
|
import { writeVersionsConfig } from '@/lib/BazookaManager'
|
||||||
|
import { openFolder } from '@/lib/Util'
|
||||||
|
|
||||||
export default function Installs () {
|
export default function Installs () {
|
||||||
const {
|
const {
|
||||||
@@ -20,7 +21,6 @@ export default function Installs () {
|
|||||||
setFadeOut,
|
setFadeOut,
|
||||||
setSelectedVersionList,
|
setSelectedVersionList,
|
||||||
downloadedVersionsConfig,
|
downloadedVersionsConfig,
|
||||||
downloadProgress,
|
|
||||||
normalConfig,
|
normalConfig,
|
||||||
setManagingVersion,
|
setManagingVersion,
|
||||||
getVersionInfo,
|
getVersionInfo,
|
||||||
@@ -34,6 +34,7 @@ export default function Installs () {
|
|||||||
} = useGlobal()
|
} = useGlobal()
|
||||||
|
|
||||||
const params = useSearchParams()
|
const params = useSearchParams()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
const id = Number(params.get('id') || 0)
|
const id = Number(params.get('id') || 0)
|
||||||
const game = serverVersionList?.games.find(g => g.id === id)
|
const game = serverVersionList?.games.find(g => g.id === id)
|
||||||
@@ -63,8 +64,28 @@ export default function Installs () {
|
|||||||
return (
|
return (
|
||||||
<div className='mx-4 mt-4'>
|
<div className='mx-4 mt-4'>
|
||||||
<div className='flex justify-between items-center mb-4'>
|
<div className='flex justify-between items-center mb-4'>
|
||||||
<p className='text-3xl'>Installs</p>
|
<p
|
||||||
|
className={`text-3xl truncate ${
|
||||||
|
category != -1
|
||||||
|
? 'w-[calc(100vw-495px)]'
|
||||||
|
: game.id == 1
|
||||||
|
? 'w-[calc(100vw-560px)]'
|
||||||
|
: 'w-[calc(100vw-440px)]'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{game.name} Installs
|
||||||
|
</p>
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2'>
|
||||||
|
<button
|
||||||
|
className='button btntheme1'
|
||||||
|
onClick={() => {
|
||||||
|
router.push('/game/berrydash/leaderboards')
|
||||||
|
}}
|
||||||
|
title='View the leaderboards for this game.'
|
||||||
|
hidden={game.id != 1}
|
||||||
|
>
|
||||||
|
Leaderboards
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
className='button btntheme1'
|
className='button btntheme1'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -92,7 +113,7 @@ export default function Installs () {
|
|||||||
<div className='downloads-container'>
|
<div className='downloads-container'>
|
||||||
<div
|
<div
|
||||||
className={`downloads-scroll ${
|
className={`downloads-scroll ${
|
||||||
platform() === 'windows'
|
platform() == 'windows'
|
||||||
? 'h-[calc(100vh-116px)]'
|
? 'h-[calc(100vh-116px)]'
|
||||||
: 'h-[calc(100vh-84px)]'
|
: 'h-[calc(100vh-84px)]'
|
||||||
}`}
|
}`}
|
||||||
@@ -108,7 +129,7 @@ export default function Installs () {
|
|||||||
if (!info) return false
|
if (!info) return false
|
||||||
|
|
||||||
if (
|
if (
|
||||||
platform() === 'linux' &&
|
platform() == 'linux' &&
|
||||||
info.wine &&
|
info.wine &&
|
||||||
!normalConfig?.settings.useWineOnUnixWhenNeeded
|
!normalConfig?.settings.useWineOnUnixWhenNeeded
|
||||||
)
|
)
|
||||||
@@ -123,21 +144,9 @@ export default function Installs () {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={key}
|
key={key}
|
||||||
className={`downloads-entry ${
|
className={'downloads-entry'}
|
||||||
normalConfig?.settings.useLegacyInteractButtons
|
title={'Click to view category'}
|
||||||
? ''
|
onClick={() => setCategory(Number(key))}
|
||||||
: 'cursor-pointer'
|
|
||||||
}`}
|
|
||||||
title={
|
|
||||||
normalConfig?.settings.useLegacyInteractButtons
|
|
||||||
? ''
|
|
||||||
: 'Click to view category'
|
|
||||||
}
|
|
||||||
onClick={() => {
|
|
||||||
if (normalConfig?.settings.useLegacyInteractButtons)
|
|
||||||
return
|
|
||||||
setCategory(Number(key))
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div className='h-18 w-screen relative'>
|
<div className='h-18 w-screen relative'>
|
||||||
<p className='text-2xl'>{value}</p>
|
<p className='text-2xl'>{value}</p>
|
||||||
@@ -156,7 +165,7 @@ export default function Installs () {
|
|||||||
const info = getVersionInfo(v)
|
const info = getVersionInfo(v)
|
||||||
if (!info) return false
|
if (!info) return false
|
||||||
if (
|
if (
|
||||||
platform() === 'linux' &&
|
platform() == 'linux' &&
|
||||||
info.wine &&
|
info.wine &&
|
||||||
!normalConfig?.settings
|
!normalConfig?.settings
|
||||||
.useWineOnUnixWhenNeeded
|
.useWineOnUnixWhenNeeded
|
||||||
@@ -171,17 +180,6 @@ export default function Installs () {
|
|||||||
})()}
|
})()}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
|
||||||
className='button absolute right-0 bottom-0'
|
|
||||||
hidden={
|
|
||||||
!normalConfig?.settings.useLegacyInteractButtons
|
|
||||||
}
|
|
||||||
title='Click to view category'
|
|
||||||
onClick={() => setCategory(Number(key))}
|
|
||||||
>
|
|
||||||
Installs
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -202,7 +200,7 @@ export default function Installs () {
|
|||||||
const info = getVersionInfo(v)
|
const info = getVersionInfo(v)
|
||||||
if (!info) return false
|
if (!info) return false
|
||||||
if (
|
if (
|
||||||
platform() === 'linux' &&
|
platform() == 'linux' &&
|
||||||
info.wine &&
|
info.wine &&
|
||||||
!normalConfig?.settings.useWineOnUnixWhenNeeded
|
!normalConfig?.settings.useWineOnUnixWhenNeeded
|
||||||
)
|
)
|
||||||
@@ -217,33 +215,73 @@ export default function Installs () {
|
|||||||
.map(entry => (
|
.map(entry => (
|
||||||
<div
|
<div
|
||||||
key={entry}
|
key={entry}
|
||||||
className={`downloads-entry ${
|
className={'downloads-entry'}
|
||||||
normalConfig?.settings.useLegacyInteractButtons ||
|
|
||||||
needsRevisionUpdate(
|
|
||||||
getVersionInfo(entry)?.lastRevision,
|
|
||||||
entry
|
|
||||||
)
|
|
||||||
? ''
|
|
||||||
: 'cursor-pointer'
|
|
||||||
}`}
|
|
||||||
title={
|
title={
|
||||||
normalConfig?.settings.useLegacyInteractButtons ||
|
'Click to launch game. Right-click to manage this version install'
|
||||||
needsRevisionUpdate(
|
|
||||||
getVersionInfo(entry)?.lastRevision,
|
|
||||||
entry
|
|
||||||
)
|
|
||||||
? ''
|
|
||||||
: 'Click to launch game. Right-click to manage this version install'
|
|
||||||
}
|
}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (
|
if (
|
||||||
normalConfig?.settings.useLegacyInteractButtons ||
|
|
||||||
needsRevisionUpdate(
|
needsRevisionUpdate(
|
||||||
getVersionInfo(entry)?.lastRevision,
|
getVersionInfo(entry)?.lastRevision,
|
||||||
entry
|
entry
|
||||||
)
|
)
|
||||||
)
|
) {
|
||||||
|
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
|
return
|
||||||
|
}
|
||||||
const verInfo = getVersionInfo(entry)
|
const verInfo = getVersionInfo(entry)
|
||||||
if (verInfo == undefined) return
|
if (verInfo == undefined) return
|
||||||
const gameInfo = getGameInfo(verInfo.game)
|
const gameInfo = getGameInfo(verInfo.game)
|
||||||
@@ -253,7 +291,7 @@ export default function Installs () {
|
|||||||
executable: verInfo.executable,
|
executable: verInfo.executable,
|
||||||
displayName: verInfo.displayName,
|
displayName: verInfo.displayName,
|
||||||
useWine: !!(
|
useWine: !!(
|
||||||
platform() === 'linux' &&
|
platform() == 'linux' &&
|
||||||
verInfo.wine &&
|
verInfo.wine &&
|
||||||
normalConfig?.settings.useWineOnUnixWhenNeeded
|
normalConfig?.settings.useWineOnUnixWhenNeeded
|
||||||
),
|
),
|
||||||
@@ -262,15 +300,6 @@ export default function Installs () {
|
|||||||
}}
|
}}
|
||||||
onContextMenu={e => {
|
onContextMenu={e => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (
|
|
||||||
normalConfig?.settings.useLegacyInteractButtons ||
|
|
||||||
needsRevisionUpdate(
|
|
||||||
getVersionInfo(entry)?.lastRevision,
|
|
||||||
entry
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
setManagingVersion(entry)
|
setManagingVersion(entry)
|
||||||
setPopupMode(2)
|
setPopupMode(2)
|
||||||
setShowPopup(true)
|
setShowPopup(true)
|
||||||
@@ -300,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.'
|
title='This version is using wine. It cannot be guarenteed to work fully and might not work at all.'
|
||||||
hidden={
|
hidden={
|
||||||
!(
|
!(
|
||||||
platform() === 'linux' &&
|
platform() == 'linux' && getVersionInfo(entry)?.wine
|
||||||
getVersionInfo(entry)?.wine
|
|
||||||
) ||
|
) ||
|
||||||
needsRevisionUpdate(
|
needsRevisionUpdate(
|
||||||
getVersionInfo(entry)?.lastRevision,
|
getVersionInfo(entry)?.lastRevision,
|
||||||
@@ -327,154 +355,6 @@ export default function Installs () {
|
|||||||
<p>Needs revision update!</p>
|
<p>Needs revision update!</p>
|
||||||
</div>
|
</div>
|
||||||
</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'
|
|
||||||
hidden={
|
|
||||||
!normalConfig?.settings.useLegacyInteractButtons ||
|
|
||||||
needsRevisionUpdate(
|
|
||||||
getVersionInfo(entry)?.lastRevision,
|
|
||||||
entry
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
setManagingVersion(entry)
|
|
||||||
setPopupMode(2)
|
|
||||||
setShowPopup(true)
|
|
||||||
setFadeOut(false)
|
|
||||||
}}
|
|
||||||
title='Click to manage this version install'
|
|
||||||
>
|
|
||||||
Manage
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className='button'
|
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
const verInfo = getVersionInfo(entry)
|
|
||||||
if (verInfo == undefined) return
|
|
||||||
const gameInfo = getGameInfo(verInfo.game)
|
|
||||||
if (gameInfo == undefined) return
|
|
||||||
invoke('launch_game', {
|
|
||||||
name: verInfo.id,
|
|
||||||
executable: verInfo.executable,
|
|
||||||
displayName: verInfo.displayName,
|
|
||||||
useWine: !!(
|
|
||||||
platform() === 'linux' &&
|
|
||||||
verInfo.wine &&
|
|
||||||
normalConfig?.settings.useWineOnUnixWhenNeeded
|
|
||||||
),
|
|
||||||
wineCommand:
|
|
||||||
normalConfig?.settings.wineOnUnixCommand
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
hidden={
|
|
||||||
!normalConfig?.settings.useLegacyInteractButtons ||
|
|
||||||
needsRevisionUpdate(
|
|
||||||
getVersionInfo(entry)?.lastRevision,
|
|
||||||
entry
|
|
||||||
)
|
|
||||||
}
|
|
||||||
title='Click to launch game'
|
|
||||||
>
|
|
||||||
Launch
|
|
||||||
</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 {
|
|
||||||
invoke('open_folder', {
|
|
||||||
name: entry
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
hidden={
|
|
||||||
!needsRevisionUpdate(
|
|
||||||
getVersionInfo(entry)?.lastRevision,
|
|
||||||
entry
|
|
||||||
)
|
|
||||||
}
|
|
||||||
disabled={downloadProgress.length != 0}
|
|
||||||
title={
|
|
||||||
downloadProgress.length != 0
|
|
||||||
? 'Cannot update while downloads are in progress '
|
|
||||||
: 'Click to update the game'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Update
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|||||||
+181
-552
@@ -1,51 +1,42 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import Sidebar from './componets/Sidebar'
|
import Sidebar from '@/componets/Sidebar'
|
||||||
import './Globals.css'
|
import './Globals.css'
|
||||||
import { DownloadProgress } from './types/DownloadProgress'
|
import { DownloadProgress } from '@/types/DownloadProgress'
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import {
|
import { faChevronLeft, faXmark } from '@fortawesome/free-solid-svg-icons'
|
||||||
faAdd,
|
|
||||||
faCheck,
|
|
||||||
faChevronLeft,
|
|
||||||
faCode,
|
|
||||||
faDownload,
|
|
||||||
faInfo,
|
|
||||||
faRemove,
|
|
||||||
faShieldHalved,
|
|
||||||
faWarning,
|
|
||||||
faXmark
|
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
|
||||||
import {
|
import {
|
||||||
readNormalConfig,
|
readNormalConfig,
|
||||||
readVersionsConfig,
|
readVersionsConfig,
|
||||||
writeVersionsConfig
|
writeVersionsConfig
|
||||||
} from './util/BazookaManager'
|
} from '@/lib/BazookaManager'
|
||||||
import { VersionsConfig } from './types/VersionsConfig'
|
import { VersionsConfig } from '@/types/VersionsConfig'
|
||||||
import { NormalConfig } from './types/NormalConfig'
|
import { NormalConfig } from '@/types/NormalConfig'
|
||||||
import { app } from '@tauri-apps/api'
|
import { app } from '@tauri-apps/api'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||||
import { GlobalProvider } from './GlobalProvider'
|
import { GlobalProvider } from './GlobalProvider'
|
||||||
import { Roboto } from 'next/font/google'
|
import { Roboto } from 'next/font/google'
|
||||||
import { ServerVersionsResponse } from './types/ServerVersionsResponse'
|
import { ServerVersionsResponse } from '@/types/ServerVersionsResponse'
|
||||||
import { GameVersion } from './types/GameVersion'
|
import { GameVersion } from '@/types/GameVersion'
|
||||||
import { Game } from './types/Game'
|
import { Game } from '@/types/Game'
|
||||||
import { listen } from '@tauri-apps/api/event'
|
import { listen } from '@tauri-apps/api/event'
|
||||||
import { usePathname } from 'next/navigation'
|
import { usePathname } from 'next/navigation'
|
||||||
import { arch, platform } from '@tauri-apps/plugin-os'
|
import { arch, platform } from '@tauri-apps/plugin-os'
|
||||||
import VersionInfo from './componets/VersionInfo'
|
import { notifyUser } from '@/lib/Notifications'
|
||||||
import prettyBytes from 'pretty-bytes'
|
|
||||||
import ProgressBar from './componets/ProgressBar'
|
|
||||||
import { notifyUser } from './util/Notifications'
|
|
||||||
import {
|
import {
|
||||||
isPermissionGranted,
|
isPermissionGranted,
|
||||||
requestPermission
|
requestPermission
|
||||||
} from '@tauri-apps/plugin-notification'
|
} from '@tauri-apps/plugin-notification'
|
||||||
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
|
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
|
||||||
|
|
||||||
|
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({
|
const roboto = Roboto({
|
||||||
subsets: ['latin']
|
subsets: ['latin']
|
||||||
})
|
})
|
||||||
@@ -59,6 +50,7 @@ export default function RootLayout ({
|
|||||||
const [loadingText, setLoadingText] = useState('Loading...')
|
const [loadingText, setLoadingText] = useState('Loading...')
|
||||||
const [outdated, setOutdated] = useState(false)
|
const [outdated, setOutdated] = useState(false)
|
||||||
const [version, setVersion] = useState<string | null>(null)
|
const [version, setVersion] = useState<string | null>(null)
|
||||||
|
const [platformName, setPlatformName] = useState<string | null>(null)
|
||||||
|
|
||||||
const [serverVersionList, setServerVersionList] =
|
const [serverVersionList, setServerVersionList] =
|
||||||
useState<null | ServerVersionsResponse>(null)
|
useState<null | ServerVersionsResponse>(null)
|
||||||
@@ -75,6 +67,8 @@ export default function RootLayout ({
|
|||||||
const [downloadProgress, setDownloadProgress] = useState<DownloadProgress[]>(
|
const [downloadProgress, setDownloadProgress] = useState<DownloadProgress[]>(
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
const [downloadQueue, setDownloadQueue] = useState<string[]>([])
|
||||||
|
const [isProcessingQueue, setIsProcessingQueue] = useState<boolean>(false)
|
||||||
const [managingVersion, setManagingVersion] = useState<string | null>(null)
|
const [managingVersion, setManagingVersion] = useState<string | null>(null)
|
||||||
const [viewingInfoFromDownloads, setViewingInfoFromDownloads] =
|
const [viewingInfoFromDownloads, setViewingInfoFromDownloads] =
|
||||||
useState<boolean>(false)
|
useState<boolean>(false)
|
||||||
@@ -84,6 +78,7 @@ export default function RootLayout ({
|
|||||||
|
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const revisionCheck = useRef(false)
|
const revisionCheck = useRef(false)
|
||||||
|
const previousQueueLength = useRef(0)
|
||||||
|
|
||||||
function getSpecialVersionsList (game?: number): GameVersion[] {
|
function getSpecialVersionsList (game?: number): GameVersion[] {
|
||||||
if (!normalConfig || !serverVersionList) return []
|
if (!normalConfig || !serverVersionList) return []
|
||||||
@@ -94,7 +89,7 @@ export default function RootLayout ({
|
|||||||
)
|
)
|
||||||
.filter(v => {
|
.filter(v => {
|
||||||
if (
|
if (
|
||||||
platform() === 'linux' &&
|
platformName == 'linux' &&
|
||||||
v.wine &&
|
v.wine &&
|
||||||
!normalConfig.settings.useWineOnUnixWhenNeeded
|
!normalConfig.settings.useWineOnUnixWhenNeeded
|
||||||
)
|
)
|
||||||
@@ -152,7 +147,7 @@ export default function RootLayout ({
|
|||||||
if (!downloadedVersionsConfig || !serverVersionList) return null
|
if (!downloadedVersionsConfig || !serverVersionList) return null
|
||||||
|
|
||||||
const allowWine =
|
const allowWine =
|
||||||
platform() !== 'linux' || normalConfig?.settings.useWineOnUnixWhenNeeded
|
platformName !== 'linux' || normalConfig?.settings.useWineOnUnixWhenNeeded
|
||||||
|
|
||||||
const installed = Object.keys(downloadedVersionsConfig.list).filter(v => {
|
const installed = Object.keys(downloadedVersionsConfig.list).filter(v => {
|
||||||
const info = getVersionInfo(v)
|
const info = getVersionInfo(v)
|
||||||
@@ -170,34 +165,18 @@ export default function RootLayout ({
|
|||||||
return { installed, total }
|
return { installed, total }
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatEtaSmart (seconds: number) {
|
const closePopup = useCallback(() => {
|
||||||
if (seconds < 60) return `${Math.floor(seconds)}s`
|
|
||||||
if (seconds < 3600)
|
|
||||||
return `${Math.floor(seconds / 60)}m ${Math.floor(seconds % 60)}s`
|
|
||||||
if (seconds < 86400) {
|
|
||||||
const h = Math.floor(seconds / 3600)
|
|
||||||
const m = Math.floor((seconds % 3600) / 60)
|
|
||||||
return `${h}h ${m}m`
|
|
||||||
}
|
|
||||||
const d = Math.floor(seconds / 86400)
|
|
||||||
const h = Math.floor((seconds % 86400) / 3600)
|
|
||||||
return `${d}d ${h}h`
|
|
||||||
}
|
|
||||||
|
|
||||||
function closePopup () {
|
|
||||||
if (popupMode == 0 && selectedGame && pathname === '/') {
|
if (popupMode == 0 && selectedGame && pathname === '/') {
|
||||||
setSelectedGame(null)
|
setSelectedGame(null)
|
||||||
setSelectedVersionList([])
|
setSelectedVersionList([])
|
||||||
} else if (viewingInfoFromDownloads) {
|
} else if (viewingInfoFromDownloads) {
|
||||||
setViewingInfoFromDownloads(false)
|
setViewingInfoFromDownloads(false)
|
||||||
setPopupMode(0)
|
setPopupMode(0)
|
||||||
} else if (popupMode == 4) {
|
|
||||||
setPopupMode(3)
|
|
||||||
} else {
|
} else {
|
||||||
setFadeOut(true)
|
setFadeOut(true)
|
||||||
setTimeout(() => setShowPopup(false), 200)
|
setTimeout(() => setShowPopup(false), 200)
|
||||||
}
|
}
|
||||||
}
|
}, [popupMode, selectedGame, pathname, viewingInfoFromDownloads])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let unlistenProgress: (() => void) | null = null
|
let unlistenProgress: (() => void) | null = null
|
||||||
@@ -253,10 +232,10 @@ export default function RootLayout ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
;(async () => {
|
;(async () => {
|
||||||
|
setPlatformName(platform())
|
||||||
const client = await app.getVersion()
|
const client = await app.getVersion()
|
||||||
setVersion(client)
|
setVersion(client)
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
setLoadingText('Checking latest version...')
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
'https://games.lncvrt.xyz/api/launcher/latest'
|
'https://games.lncvrt.xyz/api/launcher/latest'
|
||||||
@@ -270,7 +249,6 @@ export default function RootLayout ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setLoadingText('Downloading version list...')
|
|
||||||
try {
|
try {
|
||||||
const res = await axios.get(
|
const res = await axios.get(
|
||||||
`https://games.lncvrt.xyz/api/launcher/versions?platform=${platform()}&arch=${arch()}`
|
`https://games.lncvrt.xyz/api/launcher/versions?platform=${platform()}&arch=${arch()}`
|
||||||
@@ -280,7 +258,6 @@ export default function RootLayout ({
|
|||||||
setLoadingText('Failed to download versions list.')
|
setLoadingText('Failed to download versions list.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setLoadingText('Loading configs...')
|
|
||||||
const normalConfig = await readNormalConfig()
|
const normalConfig = await readNormalConfig()
|
||||||
const versionsConfig = await readVersionsConfig()
|
const versionsConfig = await readVersionsConfig()
|
||||||
setDownloadedVersionsConfig(versionsConfig)
|
setDownloadedVersionsConfig(versionsConfig)
|
||||||
@@ -293,103 +270,134 @@ 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(
|
const downloadVersions = useCallback(
|
||||||
async (list: string[]): Promise<void> => {
|
async (list: string[]): Promise<void> => {
|
||||||
if (list.length === 0) return
|
if (list.length === 0) return
|
||||||
setSelectedVersionList([])
|
setSelectedVersionList([])
|
||||||
|
|
||||||
const newDownloads = list.map(
|
const newVersions = list.filter(
|
||||||
|
version =>
|
||||||
|
!downloadQueue.includes(version) &&
|
||||||
|
!downloadProgress.some(d => d.version === version)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (newVersions.length === 0) return
|
||||||
|
|
||||||
|
const newDownloads = newVersions.map(
|
||||||
version =>
|
version =>
|
||||||
new DownloadProgress(version, 0, 0, false, true, false, false, 0, 0)
|
new DownloadProgress(version, 0, 0, false, true, false, false, 0, 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
setDownloadProgress(newDownloads)
|
setDownloadProgress(prev => [...prev, ...newDownloads])
|
||||||
|
setDownloadQueue(prev => [...prev, ...newVersions])
|
||||||
|
},
|
||||||
|
[downloadQueue, downloadProgress]
|
||||||
|
)
|
||||||
|
|
||||||
for (const download of newDownloads) {
|
useEffect(() => {
|
||||||
const info = getVersionInfo(download.version)
|
if (isProcessingQueue || downloadQueue.length === 0) return
|
||||||
if (!info) {
|
|
||||||
setDownloadProgress(prev =>
|
|
||||||
prev.filter(d => d.version !== download.version)
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const gameInfo = getGameInfo(info.game)
|
const processNextDownload = async () => {
|
||||||
if (!gameInfo) {
|
setIsProcessingQueue(true)
|
||||||
setDownloadProgress(prev =>
|
|
||||||
prev.filter(d => d.version !== download.version)
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
setDownloadProgress(prev =>
|
const versionId = downloadQueue[0]
|
||||||
prev.map(d =>
|
const info = getVersionInfo(versionId)
|
||||||
d.version === download.version ? { ...d, queued: false } : d
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
if (!info) {
|
||||||
await axios.get(
|
setDownloadProgress(prev => prev.filter(d => d.version !== versionId))
|
||||||
'https://games.lncvrt.xyz/api/launcher/download?id=' + info.id
|
setDownloadQueue(prev => prev.slice(1))
|
||||||
)
|
setIsProcessingQueue(false)
|
||||||
} catch {}
|
return
|
||||||
|
|
||||||
const res = await invoke<string>('download', {
|
|
||||||
url: info.downloadUrl,
|
|
||||||
name: info.id,
|
|
||||||
executable: info.executable,
|
|
||||||
hash: info.sha512sum
|
|
||||||
})
|
|
||||||
|
|
||||||
if (res === '1') {
|
|
||||||
setDownloadProgress(prev =>
|
|
||||||
prev.filter(d => d.version !== download.version)
|
|
||||||
)
|
|
||||||
setDownloadedVersionsConfig(prev => {
|
|
||||||
if (!prev) return prev
|
|
||||||
|
|
||||||
const updated = {
|
|
||||||
...prev,
|
|
||||||
list: {
|
|
||||||
...prev.list,
|
|
||||||
[download.version]: Date.now()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeVersionsConfig(updated)
|
|
||||||
return updated
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
setDownloadProgress(prev =>
|
|
||||||
prev.map(d =>
|
|
||||||
d.version === download.version
|
|
||||||
? { ...d, queued: false, failed: true, progress: 0 }
|
|
||||||
: d
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (normalConfig?.settings.allowNotifications)
|
|
||||||
await notifyUser(
|
|
||||||
'Download Failed',
|
|
||||||
`The download for version ${info.displayName} has failed.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (normalConfig?.settings.allowNotifications)
|
const gameInfo = getGameInfo(info.game)
|
||||||
await notifyUser('Downloads Finished', 'All downloads have finished.')
|
if (!gameInfo) {
|
||||||
|
setDownloadProgress(prev => prev.filter(d => d.version !== versionId))
|
||||||
|
setDownloadQueue(prev => prev.slice(1))
|
||||||
|
setIsProcessingQueue(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
setFadeOut(true)
|
setDownloadProgress(prev =>
|
||||||
setTimeout(() => setShowPopup(false), 200)
|
prev.map(d => (d.version === versionId ? { ...d, queued: false } : d))
|
||||||
},
|
)
|
||||||
[getGameInfo, getVersionInfo, normalConfig]
|
|
||||||
)
|
try {
|
||||||
|
await axios.get(
|
||||||
|
'https://games.lncvrt.xyz/api/launcher/download?id=' + info.id
|
||||||
|
)
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
const res = await invoke<string>('download', {
|
||||||
|
url: info.downloadUrl,
|
||||||
|
name: info.id,
|
||||||
|
executable: info.executable,
|
||||||
|
hash: info.sha512sum
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res === '1') {
|
||||||
|
setDownloadProgress(prev => prev.filter(d => d.version !== versionId))
|
||||||
|
setDownloadedVersionsConfig(prev => {
|
||||||
|
if (!prev) return prev
|
||||||
|
|
||||||
|
const updated = {
|
||||||
|
...prev,
|
||||||
|
list: {
|
||||||
|
...prev.list,
|
||||||
|
[versionId]: Date.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeVersionsConfig(updated)
|
||||||
|
return updated
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setDownloadProgress(prev =>
|
||||||
|
prev.map(d =>
|
||||||
|
d.version === versionId
|
||||||
|
? { ...d, queued: false, failed: true, progress: 0 }
|
||||||
|
: d
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (normalConfig?.settings.allowNotifications)
|
||||||
|
await notifyUser(
|
||||||
|
'Download Failed',
|
||||||
|
`The download for version ${info.displayName} has failed.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
setDownloadQueue(prev => prev.slice(1))
|
||||||
|
setIsProcessingQueue(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
processNextDownload()
|
||||||
|
}, [
|
||||||
|
downloadQueue,
|
||||||
|
isProcessingQueue,
|
||||||
|
getVersionInfo,
|
||||||
|
getGameInfo,
|
||||||
|
normalConfig
|
||||||
|
])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
downloadQueue.length === 0 &&
|
||||||
|
downloadProgress.length === 0 &&
|
||||||
|
!isProcessingQueue &&
|
||||||
|
previousQueueLength.current > 0 &&
|
||||||
|
normalConfig?.settings.allowNotifications
|
||||||
|
) {
|
||||||
|
notifyUser('Downloads Finished', 'All downloads have finished.')
|
||||||
|
setTimeout(() => closePopup(), 0)
|
||||||
|
}
|
||||||
|
previousQueueLength.current = downloadQueue.length + downloadProgress.length
|
||||||
|
}, [
|
||||||
|
downloadQueue,
|
||||||
|
downloadProgress,
|
||||||
|
isProcessingQueue,
|
||||||
|
normalConfig,
|
||||||
|
closePopup
|
||||||
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (revisionCheck.current) return
|
if (revisionCheck.current) return
|
||||||
@@ -434,26 +442,38 @@ export default function RootLayout ({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className='w-screen h-screen flex items-center justify-center'>
|
<>
|
||||||
{outdated ? (
|
<div
|
||||||
<div className='text-center'>
|
className='relative z-2 w-screen border-b border-b-(--col3) h-8.25 bg-(--col1)'
|
||||||
<p className='text-8xl mb-4'>Outdated Launcher!</p>
|
hidden={platformName != 'windows'}
|
||||||
<p className='text-4xl mb-4'>
|
/>
|
||||||
Please update to the latest version to continue.
|
<div
|
||||||
</p>
|
className={`w-screen ${
|
||||||
<button
|
platformName == 'windows'
|
||||||
className='button'
|
? 'h-[calc(100vh-64px)]'
|
||||||
onClick={() =>
|
: 'h-screen'
|
||||||
openUrl('https://games.lncvrt.xyz/berrydash/download')
|
} flex items-center justify-center`}
|
||||||
}
|
>
|
||||||
>
|
{outdated ? (
|
||||||
Download latest version
|
<div className='text-center'>
|
||||||
</button>
|
<p className='text-8xl mb-4'>Outdated Launcher!</p>
|
||||||
</div>
|
<p className='text-4xl mb-4'>
|
||||||
) : (
|
Please update to the latest version to continue.
|
||||||
<p className='text-7xl text-center'>{loadingText}</p>
|
</p>
|
||||||
)}
|
<button
|
||||||
</div>
|
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
|
<GlobalProvider
|
||||||
value={{
|
value={{
|
||||||
@@ -483,7 +503,13 @@ export default function RootLayout ({
|
|||||||
version,
|
version,
|
||||||
downloadVersions,
|
downloadVersions,
|
||||||
category,
|
category,
|
||||||
setCategory
|
setCategory,
|
||||||
|
downloadQueue,
|
||||||
|
setDownloadQueue,
|
||||||
|
closePopup,
|
||||||
|
getSpecialVersionsList,
|
||||||
|
selectedGame,
|
||||||
|
setViewingInfoFromDownloads
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -494,10 +520,8 @@ export default function RootLayout ({
|
|||||||
>
|
>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
className='relative z-2 ml-59.75 w-[calc(100vw-239px)] border-b border-(--col3) h-8.25 bg-(--col1)'
|
className='relative z-2 ml-59.75 w-[calc(100vw-239px)] border-b border-b-(--col3) h-8.25 bg-(--col1)'
|
||||||
style={{
|
hidden={platformName != 'windows'}
|
||||||
display: platform() === 'windows' ? 'block' : 'none'
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<div className='relative z-0'>
|
<div className='relative z-0'>
|
||||||
<main className='ml-60'>{children}</main>
|
<main className='ml-60'>{children}</main>
|
||||||
@@ -536,409 +560,14 @@ export default function RootLayout ({
|
|||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
{popupMode === 0 && selectedGame ? (
|
{popupMode === 0 && selectedGame ? (
|
||||||
<>
|
<VersionsDownloadPopup />
|
||||||
<p className='text-xl text-center'>
|
|
||||||
Select versions to download
|
|
||||||
</p>
|
|
||||||
<div className='popup-content'>
|
|
||||||
{getSpecialVersionsList(selectedGame).map(
|
|
||||||
(v, i) => (
|
|
||||||
<div key={i} className='popup-entry'>
|
|
||||||
<div className='flex items-center'>
|
|
||||||
<p
|
|
||||||
className={`text-2xl truncate ${
|
|
||||||
selectedVersionList.includes(v.id)
|
|
||||||
? 'max-w-84.5'
|
|
||||||
: 'max-w-91.5'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{v.displayName}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className='button btntheme3 right-20.75 bottom-1.75'
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedVersionList(prev =>
|
|
||||||
prev.includes(v.id)
|
|
||||||
? prev.filter(i => i !== v.id)
|
|
||||||
: [...prev, v.id]
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
title={
|
|
||||||
selectedVersionList.includes(v.id)
|
|
||||||
? 'This version will be downloaded. Click to remove from the list of versions that will be downloaded.'
|
|
||||||
: 'This version will NOT be downloaded. Click to add from the list of versions that will be downloaded.'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{selectedVersionList.includes(v.id) ? (
|
|
||||||
<>
|
|
||||||
<FontAwesomeIcon icon={faRemove} />{' '}
|
|
||||||
Remove
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<FontAwesomeIcon icon={faAdd} /> Add
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className='button btntheme3 right-1.5 bottom-1.75'
|
|
||||||
onClick={() => {
|
|
||||||
setManagingVersion(v.id)
|
|
||||||
setViewingInfoFromDownloads(true)
|
|
||||||
setPopupMode(3)
|
|
||||||
}}
|
|
||||||
title='Click to view version info'
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faInfo} /> Info
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : popupMode === 0 && !selectedGame ? (
|
) : popupMode === 0 && !selectedGame ? (
|
||||||
<>
|
<GamesDownloadPopup />
|
||||||
<p className='text-xl text-center'>
|
|
||||||
Select a game to download
|
|
||||||
</p>
|
|
||||||
<div className='popup-content'>
|
|
||||||
{serverVersionList?.games
|
|
||||||
.filter(v => {
|
|
||||||
const data = getVersionsAmountData(v.id)
|
|
||||||
if (!data) return false
|
|
||||||
if (data.total > 0) return true
|
|
||||||
})
|
|
||||||
.map((v, i) => (
|
|
||||||
<div key={i} className='popup-entry'>
|
|
||||||
<p className='text-2xl'>{v.name}</p>
|
|
||||||
<div className='flex gap-2'>
|
|
||||||
<div
|
|
||||||
className='entry-info-item btntheme3'
|
|
||||||
title='The amount of versions installed of this game in installed/installable format.'
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
{(() => {
|
|
||||||
const data = getVersionsAmountData(
|
|
||||||
v.id
|
|
||||||
)
|
|
||||||
if (!data) return 'N/A'
|
|
||||||
return `${data.installed}/${data.total}`
|
|
||||||
})()}{' '}
|
|
||||||
versions installed
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className='entry-info-item btntheme3'
|
|
||||||
hidden={!v.official}
|
|
||||||
title='This game is official.'
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={faCheck}
|
|
||||||
color='#19c84b'
|
|
||||||
/>
|
|
||||||
<p>Official</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className='entry-info-item btntheme3'
|
|
||||||
hidden={v.official}
|
|
||||||
title={
|
|
||||||
v.verified
|
|
||||||
? 'This game is verified to be safe'
|
|
||||||
: 'This game is NOT verified to be save. Proceed with caution.'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={
|
|
||||||
v.verified
|
|
||||||
? faShieldHalved
|
|
||||||
: faWarning
|
|
||||||
}
|
|
||||||
color={
|
|
||||||
v.verified ? '#19c84b' : '#ffc800'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<p>
|
|
||||||
{v.verified ? 'Verified' : 'Unverified'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className='entry-info-item btntheme3 mt-2'
|
|
||||||
hidden={v.developer == null}
|
|
||||||
title={`The developer of ${v.name} is ${v.developer}.`}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={faCode}
|
|
||||||
color='lightgray'
|
|
||||||
/>
|
|
||||||
<p>Developer: {v.developer}</p>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className='button btntheme3 right-2 bottom-2'
|
|
||||||
onClick={() => setSelectedGame(v.id)}
|
|
||||||
title={`Click to download specific versions of the game. You have ${(() => {
|
|
||||||
const data = getVersionsAmountData(v.id)
|
|
||||||
if (!data) return 'N/A'
|
|
||||||
return `${data.installed} of ${data.total}`
|
|
||||||
})()} versions downloaded.`}
|
|
||||||
>
|
|
||||||
<>
|
|
||||||
<FontAwesomeIcon icon={faDownload} />{' '}
|
|
||||||
Download
|
|
||||||
</>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : popupMode === 1 ? (
|
) : popupMode === 1 ? (
|
||||||
<>
|
<DownloadsPopup />
|
||||||
<p className='text-xl text-center'>Downloads</p>
|
|
||||||
<div className='popup-content'>
|
|
||||||
{downloadProgress.map((v, i) => (
|
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
className='popup-entry flex flex-col justify-between'
|
|
||||||
>
|
|
||||||
<p className='text-2xl text-center'>
|
|
||||||
{getVersionInfo(v.version)?.displayName}
|
|
||||||
</p>
|
|
||||||
<div className='mt-6.25 flex items-center justify-between'>
|
|
||||||
{v.failed ? (
|
|
||||||
<>
|
|
||||||
<div className='flex items-center'>
|
|
||||||
<span className='text-red-500 inline-block w-full text-center'>
|
|
||||||
Download failed
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
className='button btntheme3 ml-30 mb-2'
|
|
||||||
onClick={() => {
|
|
||||||
setDownloadProgress(prev =>
|
|
||||||
prev.filter(
|
|
||||||
d => d.version !== v.version
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
title='Click to remove this version from this menu.'
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : v.queued ? (
|
|
||||||
<span className='text-yellow-500 inline-block w-full text-center'>
|
|
||||||
Queued…
|
|
||||||
</span>
|
|
||||||
) : v.queued ? (
|
|
||||||
<span className='text-yellow-500 inline-block w-full text-center'>
|
|
||||||
Queued…
|
|
||||||
</span>
|
|
||||||
) : v.hash_checking ? (
|
|
||||||
<span className='text-blue-500 inline-block w-full text-center'>
|
|
||||||
Checking hash...
|
|
||||||
</span>
|
|
||||||
) : v.finishing ? (
|
|
||||||
<span className='text-green-500 inline-block w-full text-center'>
|
|
||||||
Finishing...
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<div className='flex flex-col gap-1 w-full'>
|
|
||||||
<span className='text-center'>
|
|
||||||
Downloaded{' '}
|
|
||||||
{prettyBytes(v.progressBytes, {
|
|
||||||
minimumFractionDigits: 1,
|
|
||||||
maximumFractionDigits: 1
|
|
||||||
})}{' '}
|
|
||||||
of{' '}
|
|
||||||
{prettyBytes(
|
|
||||||
getVersionInfo(v.version)?.size ?? 0,
|
|
||||||
{
|
|
||||||
minimumFractionDigits: 1,
|
|
||||||
maximumFractionDigits: 1
|
|
||||||
}
|
|
||||||
)}{' '}
|
|
||||||
(ETA: {formatEtaSmart(v.etaSecs)} •
|
|
||||||
Speed:{' '}
|
|
||||||
{prettyBytes(v.speed, {
|
|
||||||
minimumFractionDigits: 1,
|
|
||||||
maximumFractionDigits: 1
|
|
||||||
})}
|
|
||||||
/s)
|
|
||||||
</span>
|
|
||||||
<ProgressBar
|
|
||||||
progress={v.progress}
|
|
||||||
className='w-full'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : popupMode === 2 ? (
|
) : popupMode === 2 ? (
|
||||||
managingVersion ? (
|
<VersionVersionPopup />
|
||||||
<>
|
|
||||||
<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'
|
|
||||||
disabled={downloadProgress.length != 0}
|
|
||||||
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'
|
|
||||||
disabled={downloadProgress.length != 0}
|
|
||||||
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 () =>
|
|
||||||
invoke('open_folder', {
|
|
||||||
name: managingVersion
|
|
||||||
})
|
|
||||||
}
|
|
||||||
title="Click to browse the game's files."
|
|
||||||
>
|
|
||||||
Open Folder
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<p className='text-xl text-center'>
|
|
||||||
No version selected
|
|
||||||
</p>
|
|
||||||
)
|
|
||||||
) : popupMode === 3 ? (
|
|
||||||
managingVersion && downloadedVersionsConfig ? (
|
|
||||||
<VersionInfo />
|
|
||||||
) : (
|
|
||||||
<p className='text-xl text-center'>
|
|
||||||
No version selected
|
|
||||||
</p>
|
|
||||||
)
|
|
||||||
) : null}
|
) : null}
|
||||||
{popupMode == 0 &&
|
|
||||||
selectedGame &&
|
|
||||||
serverVersionList != null && (
|
|
||||||
<div className='flex justify-center'>
|
|
||||||
<button
|
|
||||||
className='button btntheme1 w-fit mt-2 -mb-4'
|
|
||||||
onClick={() => {
|
|
||||||
setFadeOut(true)
|
|
||||||
setTimeout(() => setShowPopup(false), 200)
|
|
||||||
if (downloadedVersionsConfig)
|
|
||||||
downloadVersions(selectedVersionList)
|
|
||||||
}}
|
|
||||||
disabled={downloadProgress.length != 0}
|
|
||||||
title={
|
|
||||||
downloadProgress.length != 0
|
|
||||||
? "You cannot download the versions as you have another download that hasn't completed."
|
|
||||||
: `Click to download ${
|
|
||||||
selectedVersionList.length
|
|
||||||
} version${
|
|
||||||
selectedVersionList.length == 1 ? '' : 's'
|
|
||||||
} of ${getGameInfo(selectedGame)?.name}`
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Download {selectedVersionList.length} version
|
|
||||||
{selectedVersionList.length == 1 ? '' : 's'}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className='button btntheme1 w-fit mt-2 ml-2 -mb-4'
|
|
||||||
onClick={() => {
|
|
||||||
const allIds = getSpecialVersionsList(
|
|
||||||
selectedGame
|
|
||||||
).map(v => v.id)
|
|
||||||
setSelectedVersionList(prev =>
|
|
||||||
prev.length === allIds.length ? [] : allIds
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
title={
|
|
||||||
selectedVersionList.length ===
|
|
||||||
getSpecialVersionsList(selectedGame).length
|
|
||||||
? 'Click to remove all selected versions for download.'
|
|
||||||
: 'Click to add all selected versions for download.'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{selectedVersionList.length ===
|
|
||||||
getSpecialVersionsList(selectedGame).length
|
|
||||||
? 'Deselect All'
|
|
||||||
: 'Select All'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+3
-24
@@ -54,7 +54,7 @@ export default function Installs () {
|
|||||||
<div className='downloads-container'>
|
<div className='downloads-container'>
|
||||||
<div
|
<div
|
||||||
className={`downloads-scroll ${
|
className={`downloads-scroll ${
|
||||||
platform() === 'windows'
|
platform() == 'windows'
|
||||||
? 'h-[calc(100vh-116px)]'
|
? 'h-[calc(100vh-116px)]'
|
||||||
: 'h-[calc(100vh-84px)]'
|
: 'h-[calc(100vh-84px)]'
|
||||||
}`}
|
}`}
|
||||||
@@ -68,18 +68,9 @@ export default function Installs () {
|
|||||||
.map(i => (
|
.map(i => (
|
||||||
<div
|
<div
|
||||||
key={i.id}
|
key={i.id}
|
||||||
className={`downloads-entry ${
|
className={'downloads-entry'}
|
||||||
normalConfig?.settings.useLegacyInteractButtons
|
title={'Click to view game installs'}
|
||||||
? ''
|
|
||||||
: 'cursor-pointer'
|
|
||||||
}`}
|
|
||||||
title={
|
|
||||||
normalConfig?.settings.useLegacyInteractButtons
|
|
||||||
? ''
|
|
||||||
: 'Click to view game installs'
|
|
||||||
}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (normalConfig?.settings.useLegacyInteractButtons) return
|
|
||||||
setCategory(-1)
|
setCategory(-1)
|
||||||
router.push('/game?id=' + i.id)
|
router.push('/game?id=' + i.id)
|
||||||
}}
|
}}
|
||||||
@@ -128,18 +119,6 @@ export default function Installs () {
|
|||||||
<p>{i.verified ? 'Verified' : 'Unverified'}</p>
|
<p>{i.verified ? 'Verified' : 'Unverified'}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
|
||||||
className='button absolute right-0 bottom-0'
|
|
||||||
hidden={!normalConfig?.settings.useLegacyInteractButtons}
|
|
||||||
title='Click to view game installs'
|
|
||||||
onClick={() => {
|
|
||||||
setCategory(-1)
|
|
||||||
router.push('/game?id=' + i.id)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Installs
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Setting } from '../componets/Setting'
|
import { Setting } from '@/componets/Setting'
|
||||||
import { writeNormalConfig } from '../util/BazookaManager'
|
import { writeNormalConfig } from '@/lib/BazookaManager'
|
||||||
import { useGlobal } from '../GlobalProvider'
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
import { copyToClipboard } from '../util/Clipboard'
|
import { copyToClipboard } from '@/lib/Clipboard'
|
||||||
import { platform } from '@tauri-apps/plugin-os'
|
import { platform } from '@tauri-apps/plugin-os'
|
||||||
|
|
||||||
export default function Settings () {
|
export default function Settings () {
|
||||||
const [allowNotifications, setAllowNotifications] = useState(false)
|
const [allowNotifications, setAllowNotifications] = useState(false)
|
||||||
const [alwaysShowGamesInSidebar, setAlwaysShowGamesInSidebar] =
|
const [alwaysShowGamesInSidebar, setAlwaysShowGamesInSidebar] =
|
||||||
useState(false)
|
useState(false)
|
||||||
const [useLegacyInteractButtons, setUseLegacyInteractButtons] =
|
|
||||||
useState(false)
|
|
||||||
const [useWineOnUnixWhenNeeded, setUseWineOnUnixWhenNeeded] = useState(false)
|
const [useWineOnUnixWhenNeeded, setUseWineOnUnixWhenNeeded] = useState(false)
|
||||||
const [wineOnUnixCommand, setWineOnUnixCommand] = useState('wine %path%')
|
const [wineOnUnixCommand, setWineOnUnixCommand] = useState('wine %path%')
|
||||||
const [theme, setTheme] = useState(0)
|
const [theme, setTheme] = useState(0)
|
||||||
@@ -27,9 +25,6 @@ export default function Settings () {
|
|||||||
setAlwaysShowGamesInSidebar(
|
setAlwaysShowGamesInSidebar(
|
||||||
normalConfig.settings.alwaysShowGamesInSidebar
|
normalConfig.settings.alwaysShowGamesInSidebar
|
||||||
)
|
)
|
||||||
setUseLegacyInteractButtons(
|
|
||||||
normalConfig.settings.useLegacyInteractButtons
|
|
||||||
)
|
|
||||||
setUseWineOnUnixWhenNeeded(
|
setUseWineOnUnixWhenNeeded(
|
||||||
normalConfig.settings.useWineOnUnixWhenNeeded
|
normalConfig.settings.useWineOnUnixWhenNeeded
|
||||||
)
|
)
|
||||||
@@ -92,29 +87,6 @@ export default function Settings () {
|
|||||||
}}
|
}}
|
||||||
title="This setting will make it so when you are on a page like this, the games won't disappear."
|
title="This setting will make it so when you are on a page like this, the games won't disappear."
|
||||||
/>
|
/>
|
||||||
<Setting
|
|
||||||
label='Show Installs/Launch/Manage Buttons'
|
|
||||||
value={useLegacyInteractButtons}
|
|
||||||
onChange={async () => {
|
|
||||||
if (!normalConfig) return
|
|
||||||
setUseLegacyInteractButtons(!useLegacyInteractButtons)
|
|
||||||
setNormalConfig({
|
|
||||||
...normalConfig,
|
|
||||||
settings: {
|
|
||||||
...normalConfig.settings,
|
|
||||||
useLegacyInteractButtons: !useLegacyInteractButtons
|
|
||||||
}
|
|
||||||
})
|
|
||||||
await writeNormalConfig({
|
|
||||||
...normalConfig,
|
|
||||||
settings: {
|
|
||||||
...normalConfig.settings,
|
|
||||||
useLegacyInteractButtons: !useLegacyInteractButtons
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
title='Enable the legacy method of using the installs/launch/manage buttons. In the future this setting may be removed so try and get used to the new method.'
|
|
||||||
/>
|
|
||||||
<Setting
|
<Setting
|
||||||
label='Use wine when needed to launch games'
|
label='Use wine when needed to launch games'
|
||||||
value={useWineOnUnixWhenNeeded}
|
value={useWineOnUnixWhenNeeded}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
export interface LeaderboardEntry {
|
|
||||||
username: string
|
|
||||||
userid: bigint
|
|
||||||
value: bigint
|
|
||||||
icon: number
|
|
||||||
overlay: number
|
|
||||||
birdColor: number[]
|
|
||||||
overlayColor: number[]
|
|
||||||
customIcon: string | null
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { LeaderboardEntry } from './LeaderboardEntry'
|
|
||||||
|
|
||||||
export interface LeaderboardResponse {
|
|
||||||
entries: LeaderboardEntry[]
|
|
||||||
customIcons: Record<string, string>
|
|
||||||
}
|
|
||||||
File renamed without changes.
File renamed without changes.
@@ -1,7 +1,7 @@
|
|||||||
import './Setting.css'
|
import './Setting.css'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faCheck } from '@fortawesome/free-solid-svg-icons'
|
import { faCheck } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { SettingProps } from '../types/SettingProps'
|
import { SettingProps } from '@/types/SettingProps'
|
||||||
|
|
||||||
export function Setting ({
|
export function Setting ({
|
||||||
label,
|
label,
|
||||||
@@ -13,10 +13,6 @@
|
|||||||
@apply bg-(--col3) border-(--col4);
|
@apply bg-(--col3) border-(--col4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
|
||||||
@apply flex items-center pl-2 h-10 w-60 pt-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-links {
|
.nav-links {
|
||||||
@apply flex flex-col px-4 space-y-1;
|
@apply flex flex-col px-4 space-y-1;
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
import { faDiscord } from '@fortawesome/free-brands-svg-icons'
|
import { faDiscord } from '@fortawesome/free-brands-svg-icons'
|
||||||
import { platform } from '@tauri-apps/plugin-os'
|
import { platform } from '@tauri-apps/plugin-os'
|
||||||
import { useGlobal } from '../GlobalProvider'
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
|
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
|
||||||
@@ -59,15 +59,10 @@ export default function Sidebar () {
|
|||||||
}}
|
}}
|
||||||
></div>
|
></div>
|
||||||
<div
|
<div
|
||||||
className='logo'
|
className={`flex items-center h-10 w-60 ${
|
||||||
style={{
|
(platform() == 'windows' ? 'pl-1 pt-1' : 'pl-2 pt-2') +
|
||||||
marginTop:
|
(platform() == 'macos' ? ' mt-7' : '')
|
||||||
platform() == 'windows'
|
}`}
|
||||||
? '32px'
|
|
||||||
: platform() == 'macos'
|
|
||||||
? '28px'
|
|
||||||
: ''
|
|
||||||
}}
|
|
||||||
onMouseDown={e => {
|
onMouseDown={e => {
|
||||||
if (platform() != 'macos') return
|
if (platform() != 'macos') return
|
||||||
if (e.buttons === 1) {
|
if (e.buttons === 1) {
|
||||||
@@ -89,7 +84,7 @@ export default function Sidebar () {
|
|||||||
draggable={false}
|
draggable={false}
|
||||||
href='/'
|
href='/'
|
||||||
className={`link relative flex items-center ${
|
className={`link relative flex items-center ${
|
||||||
pathname === '/' || pathname === '/game' ? 'active' : ''
|
pathname === '/' || pathname.startsWith('/game') ? 'active' : ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faHexagonNodes} className='mr-2' /> Games
|
<FontAwesomeIcon icon={faHexagonNodes} className='mr-2' /> Games
|
||||||
@@ -103,13 +98,15 @@ export default function Sidebar () {
|
|||||||
<div
|
<div
|
||||||
draggable={false}
|
draggable={false}
|
||||||
className={`link ${
|
className={`link ${
|
||||||
pathname === '/game' && Number(params.get('id') || 0) == i.id
|
(pathname === '/game' &&
|
||||||
|
Number(params.get('id') || 0) == i.id) ||
|
||||||
|
(i.id == 1 && pathname === '/game/berrydash/leaderboards')
|
||||||
? 'active'
|
? 'active'
|
||||||
: ''
|
: ''
|
||||||
} ml-auto w-50 ${
|
} ml-auto w-50 ${
|
||||||
normalConfig?.settings.alwaysShowGamesInSidebar ||
|
normalConfig?.settings.alwaysShowGamesInSidebar ||
|
||||||
pathname === '/' ||
|
pathname === '/' ||
|
||||||
pathname === '/game'
|
pathname.startsWith('/game')
|
||||||
? ''
|
? ''
|
||||||
: 'hidden'
|
: 'hidden'
|
||||||
}`}
|
}`}
|
||||||
@@ -140,7 +137,7 @@ export default function Sidebar () {
|
|||||||
if (!info) return false
|
if (!info) return false
|
||||||
|
|
||||||
if (
|
if (
|
||||||
platform() === 'linux' &&
|
platform() == 'linux' &&
|
||||||
info.wine &&
|
info.wine &&
|
||||||
!normalConfig?.settings.useWineOnUnixWhenNeeded
|
!normalConfig?.settings.useWineOnUnixWhenNeeded
|
||||||
)
|
)
|
||||||
@@ -156,15 +153,17 @@ export default function Sidebar () {
|
|||||||
key={`${i.id}-${key}`}
|
key={`${i.id}-${key}`}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
className={`link ${
|
className={`link ${
|
||||||
pathname === '/game' &&
|
(pathname === '/game' &&
|
||||||
Number(params.get('id') || 0) == i.id &&
|
Number(params.get('id') || 0) == i.id) ||
|
||||||
category == Number(key)
|
(i.id == 1 &&
|
||||||
|
pathname === '/game/berrydash/leaderboards' &&
|
||||||
|
category == Number(key))
|
||||||
? 'active'
|
? 'active'
|
||||||
: ''
|
: ''
|
||||||
} ml-auto w-47.5 ${
|
} ml-auto w-47.5 ${
|
||||||
normalConfig?.settings.alwaysShowGamesInSidebar ||
|
normalConfig?.settings.alwaysShowGamesInSidebar ||
|
||||||
pathname === '/' ||
|
pathname === '/' ||
|
||||||
pathname === '/game'
|
pathname.startsWith('/game')
|
||||||
? ''
|
? ''
|
||||||
: 'hidden'
|
: 'hidden'
|
||||||
}`}
|
}`}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
|
import prettyBytes from 'pretty-bytes'
|
||||||
|
import ProgressBar from '../ProgressBar'
|
||||||
|
import { formatEtaSmart } from '@/lib/Util'
|
||||||
|
|
||||||
|
export default function DownloadsPopup () {
|
||||||
|
const {
|
||||||
|
downloadProgress,
|
||||||
|
getVersionInfo,
|
||||||
|
setDownloadProgress,
|
||||||
|
downloadQueue,
|
||||||
|
setDownloadQueue
|
||||||
|
} = useGlobal()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className='text-xl text-center'>Downloads</p>
|
||||||
|
<div className='popup-content'>
|
||||||
|
{downloadProgress.map((v, i) => {
|
||||||
|
const queuePosition = downloadQueue.indexOf(v.version)
|
||||||
|
return (
|
||||||
|
<div key={i} className='popup-entry flex flex-col justify-between'>
|
||||||
|
<p className='text-2xl text-center'>
|
||||||
|
{getVersionInfo(v.version)?.displayName}
|
||||||
|
</p>
|
||||||
|
<div className='mt-6.25 flex items-center justify-between'>
|
||||||
|
{v.failed || v.queued ? (
|
||||||
|
<div className='flex items-center justify-between w-full'>
|
||||||
|
<span
|
||||||
|
className={`${
|
||||||
|
v.failed ? 'text-red-500' : 'text-yellow-300'
|
||||||
|
} inline-block text-center flex-1`}
|
||||||
|
>
|
||||||
|
{v.failed
|
||||||
|
? 'Download failed'
|
||||||
|
: queuePosition === 0
|
||||||
|
? 'Starting soon...'
|
||||||
|
: `Queued (Position ${queuePosition + 1})`}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
className='button btntheme3 -ml-1.25'
|
||||||
|
onClick={() => {
|
||||||
|
setDownloadQueue(prev =>
|
||||||
|
prev.filter(id => id !== v.version)
|
||||||
|
)
|
||||||
|
setDownloadProgress(prev =>
|
||||||
|
prev.filter(d => d.version !== v.version)
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
title='Click to remove this version from the download queue.'
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : v.hash_checking || v.finishing ? (
|
||||||
|
<span
|
||||||
|
className={`${
|
||||||
|
v.hash_checking ? 'text-blue-300' : 'text-green-300'
|
||||||
|
} inline-block w-full text-center`}
|
||||||
|
>
|
||||||
|
{v.hash_checking ? 'Verifying file integerty' : 'Finishing'}
|
||||||
|
...
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<div className='flex flex-col gap-1 w-full'>
|
||||||
|
<span className='text-center'>
|
||||||
|
Downloaded{' '}
|
||||||
|
{prettyBytes(v.progressBytes, {
|
||||||
|
minimumFractionDigits: 1,
|
||||||
|
maximumFractionDigits: 1
|
||||||
|
})}{' '}
|
||||||
|
of{' '}
|
||||||
|
{prettyBytes(getVersionInfo(v.version)?.size ?? 0, {
|
||||||
|
minimumFractionDigits: 1,
|
||||||
|
maximumFractionDigits: 1
|
||||||
|
})}{' '}
|
||||||
|
(ETA: {formatEtaSmart(v.etaSecs)} • Speed:{' '}
|
||||||
|
{prettyBytes(v.speed, {
|
||||||
|
minimumFractionDigits: 1,
|
||||||
|
maximumFractionDigits: 1
|
||||||
|
})}
|
||||||
|
/s)
|
||||||
|
</span>
|
||||||
|
<ProgressBar progress={v.progress} className='w-full' />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
|
import {
|
||||||
|
faCheck,
|
||||||
|
faCode,
|
||||||
|
faDownload,
|
||||||
|
faShieldHalved,
|
||||||
|
faWarning
|
||||||
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
|
||||||
|
export default function GamesDownloadPopup () {
|
||||||
|
const { serverVersionList, getVersionsAmountData, setSelectedGame } =
|
||||||
|
useGlobal()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className='text-xl text-center'>Select a game to download</p>
|
||||||
|
<div className='popup-content'>
|
||||||
|
{serverVersionList?.games
|
||||||
|
.filter(v => {
|
||||||
|
const data = getVersionsAmountData(v.id)
|
||||||
|
if (!data) return false
|
||||||
|
if (data.total > 0) return true
|
||||||
|
})
|
||||||
|
.map((v, i) => (
|
||||||
|
<div key={i} className='popup-entry'>
|
||||||
|
<p className='text-2xl'>{v.name}</p>
|
||||||
|
<div className='flex gap-2'>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme3'
|
||||||
|
title='The amount of versions installed of this game in installed/installable format.'
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
{(() => {
|
||||||
|
const data = getVersionsAmountData(v.id)
|
||||||
|
if (!data) return 'N/A'
|
||||||
|
return `${data.installed}/${data.total}`
|
||||||
|
})()}{' '}
|
||||||
|
versions installed
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme3'
|
||||||
|
hidden={!v.official}
|
||||||
|
title='This game is official.'
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faCheck} color='#19c84b' />
|
||||||
|
<p>Official</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme3'
|
||||||
|
hidden={v.official}
|
||||||
|
title={
|
||||||
|
v.verified
|
||||||
|
? 'This game is verified to be safe'
|
||||||
|
: 'This game is NOT verified to be save. Proceed with caution.'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={v.verified ? faShieldHalved : faWarning}
|
||||||
|
color={v.verified ? '#19c84b' : '#ffc800'}
|
||||||
|
/>
|
||||||
|
<p>{v.verified ? 'Verified' : 'Unverified'}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme3 mt-2'
|
||||||
|
hidden={v.developer == null}
|
||||||
|
title={`The developer of ${v.name} is ${v.developer}.`}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faCode} color='lightgray' />
|
||||||
|
<p>Developer: {v.developer}</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className='button btntheme3 right-2 bottom-2'
|
||||||
|
onClick={() => setSelectedGame(v.id)}
|
||||||
|
title={`Click to download specific versions of the game. You have ${(() => {
|
||||||
|
const data = getVersionsAmountData(v.id)
|
||||||
|
if (!data) return 'N/A'
|
||||||
|
return `${data.installed} of ${data.total}`
|
||||||
|
})()} versions downloaded.`}
|
||||||
|
>
|
||||||
|
<>
|
||||||
|
<FontAwesomeIcon icon={faDownload} /> Download
|
||||||
|
</>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -9,29 +9,39 @@ import {
|
|||||||
faWarning
|
faWarning
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { useGlobal } from '../GlobalProvider'
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import prettyBytes from 'pretty-bytes'
|
import prettyBytes from 'pretty-bytes'
|
||||||
import { message } from '@tauri-apps/plugin-dialog'
|
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 VersionInfo () {
|
export default function VersionVersionPopup () {
|
||||||
const {
|
const {
|
||||||
getGameInfo,
|
getGameInfo,
|
||||||
getVersionInfo,
|
getVersionInfo,
|
||||||
managingVersion,
|
managingVersion,
|
||||||
downloadedVersionsConfig,
|
downloadedVersionsConfig,
|
||||||
viewingInfoFromDownloads
|
viewingInfoFromDownloads,
|
||||||
|
setManagingVersion,
|
||||||
|
closePopup,
|
||||||
|
setDownloadedVersionsConfig,
|
||||||
|
setPopupMode,
|
||||||
|
setSelectedVersionList,
|
||||||
|
downloadVersions
|
||||||
} = useGlobal()
|
} = useGlobal()
|
||||||
const [versionSize, setVersionSize] = useState<number>(0)
|
const [versionSize, setVersionSize] = useState<number>(0)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (viewingInfoFromDownloads) return
|
||||||
invoke<string>('folder_size', {
|
invoke<string>('folder_size', {
|
||||||
version: managingVersion
|
version: managingVersion
|
||||||
}).then(size => {
|
}).then(size => {
|
||||||
setVersionSize(Number(size))
|
setVersionSize(Number(size))
|
||||||
})
|
})
|
||||||
}, [managingVersion, setVersionSize])
|
}, [managingVersion, setVersionSize, viewingInfoFromDownloads])
|
||||||
|
|
||||||
if (!managingVersion || !downloadedVersionsConfig) return <></>
|
if (!managingVersion || !downloadedVersionsConfig) return <></>
|
||||||
|
|
||||||
@@ -40,9 +50,7 @@ export default function VersionInfo () {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p className='text-xl text-center'>
|
<p className='text-xl text-center'>Viewing {versionInfo?.displayName}</p>
|
||||||
Viewing info for {versionInfo?.displayName}
|
|
||||||
</p>
|
|
||||||
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
|
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
|
||||||
<div
|
<div
|
||||||
className='entry-info-item btntheme2'
|
className='entry-info-item btntheme2'
|
||||||
@@ -86,12 +94,12 @@ export default function VersionInfo () {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className='entry-info-item btntheme2'
|
className='entry-info-item btntheme2'
|
||||||
hidden={viewingInfoFromDownloads || versionSize === null}
|
hidden={viewingInfoFromDownloads}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
|
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
|
||||||
<p>
|
<p>
|
||||||
Size on disk:{' '}
|
Size on disk:{' '}
|
||||||
{versionSize > 0
|
{versionSize && versionSize > 0
|
||||||
? prettyBytes(versionSize, {
|
? prettyBytes(versionSize, {
|
||||||
minimumFractionDigits: 2,
|
minimumFractionDigits: 2,
|
||||||
maximumFractionDigits: 2
|
maximumFractionDigits: 2
|
||||||
@@ -99,7 +107,10 @@ export default function VersionInfo () {
|
|||||||
: 'N/A'}
|
: 'N/A'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='entry-info-item btntheme2' hidden={!versionInfo}>
|
<div
|
||||||
|
className='entry-info-item btntheme2'
|
||||||
|
hidden={!viewingInfoFromDownloads}
|
||||||
|
>
|
||||||
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
|
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
|
||||||
<p>
|
<p>
|
||||||
Size when downloaded (zipped):{' '}
|
Size when downloaded (zipped):{' '}
|
||||||
@@ -125,6 +136,88 @@ export default function VersionInfo () {
|
|||||||
<p>View Changelog</p>
|
<p>View Changelog</p>
|
||||||
<FontAwesomeIcon icon={faArrowUpRightFromSquare} color='lightgray' />
|
<FontAwesomeIcon icon={faArrowUpRightFromSquare} color='lightgray' />
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
|
import { faAdd, faInfo, faRemove } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
|
||||||
|
export default function VersionsDownloadPopup () {
|
||||||
|
const {
|
||||||
|
selectedVersionList,
|
||||||
|
setSelectedVersionList,
|
||||||
|
setManagingVersion,
|
||||||
|
setPopupMode,
|
||||||
|
getSpecialVersionsList,
|
||||||
|
selectedGame,
|
||||||
|
setViewingInfoFromDownloads,
|
||||||
|
downloadedVersionsConfig,
|
||||||
|
downloadVersions,
|
||||||
|
getGameInfo
|
||||||
|
} = useGlobal()
|
||||||
|
if (!selectedGame) return <></>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className='text-xl text-center'>Select versions to download</p>
|
||||||
|
<div className='popup-content'>
|
||||||
|
{getSpecialVersionsList(selectedGame).map((v, i) => (
|
||||||
|
<div key={i} className='popup-entry'>
|
||||||
|
<div className='flex items-center'>
|
||||||
|
<p
|
||||||
|
className={`text-2xl truncate ${
|
||||||
|
selectedVersionList.includes(v.id)
|
||||||
|
? 'max-w-84.5'
|
||||||
|
: 'max-w-91.5'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{v.displayName}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className='button btntheme3 right-20.75 bottom-1.75'
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedVersionList(prev =>
|
||||||
|
prev.includes(v.id)
|
||||||
|
? prev.filter(i => i !== v.id)
|
||||||
|
: [...prev, v.id]
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
title={
|
||||||
|
selectedVersionList.includes(v.id)
|
||||||
|
? 'This version will be downloaded. Click to remove from the list of versions that will be downloaded.'
|
||||||
|
: 'This version will NOT be downloaded. Click to add from the list of versions that will be downloaded.'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{selectedVersionList.includes(v.id) ? (
|
||||||
|
<>
|
||||||
|
<FontAwesomeIcon icon={faRemove} /> Remove
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<FontAwesomeIcon icon={faAdd} /> Add
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='button btntheme3 right-1.5 bottom-1.75'
|
||||||
|
onClick={() => {
|
||||||
|
setManagingVersion(v.id)
|
||||||
|
setViewingInfoFromDownloads(true)
|
||||||
|
setPopupMode(2)
|
||||||
|
}}
|
||||||
|
title='Click to view version info'
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faInfo} /> Info
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className='flex justify-center'>
|
||||||
|
<button
|
||||||
|
className='button btntheme1 w-fit mt-2 -mb-4'
|
||||||
|
onClick={() => {
|
||||||
|
if (downloadedVersionsConfig) {
|
||||||
|
downloadVersions(selectedVersionList)
|
||||||
|
setPopupMode(1)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={selectedVersionList.length == 0}
|
||||||
|
title={
|
||||||
|
selectedVersionList.length == 0
|
||||||
|
? 'Select at least one version to download'
|
||||||
|
: `Download ${selectedVersionList.length} version${
|
||||||
|
selectedVersionList.length == 1 ? '' : 's'
|
||||||
|
} of ${getGameInfo(selectedGame)?.name}`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Download {selectedVersionList.length} version
|
||||||
|
{selectedVersionList.length == 1 ? '' : 's'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='button btntheme1 w-fit mt-2 ml-2 -mb-4'
|
||||||
|
onClick={() => {
|
||||||
|
const allIds = getSpecialVersionsList(selectedGame).map(v => v.id)
|
||||||
|
setSelectedVersionList(prev =>
|
||||||
|
prev.length === allIds.length ? [] : allIds
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
title={
|
||||||
|
selectedVersionList.length ===
|
||||||
|
getSpecialVersionsList(selectedGame).length
|
||||||
|
? 'Click to remove all selected versions for download.'
|
||||||
|
: 'Click to add all selected versions for download.'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{selectedVersionList.length ===
|
||||||
|
getSpecialVersionsList(selectedGame).length
|
||||||
|
? 'Deselect All'
|
||||||
|
: 'Select All'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { app } from '@tauri-apps/api'
|
import { app } from '@tauri-apps/api'
|
||||||
import { NormalConfig } from '../types/NormalConfig'
|
import { NormalConfig } from '@/types/NormalConfig'
|
||||||
import {
|
import {
|
||||||
BaseDirectory,
|
BaseDirectory,
|
||||||
create,
|
create,
|
||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
readTextFile,
|
readTextFile,
|
||||||
writeFile
|
writeFile
|
||||||
} from '@tauri-apps/plugin-fs'
|
} from '@tauri-apps/plugin-fs'
|
||||||
import { VersionsConfig } from '../types/VersionsConfig'
|
import { VersionsConfig } from '@/types/VersionsConfig'
|
||||||
|
|
||||||
export async function readNormalConfig (): Promise<NormalConfig> {
|
export async function readNormalConfig (): Promise<NormalConfig> {
|
||||||
const version = await app.getVersion()
|
const version = await app.getVersion()
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export const GetIconForUser = (user: number) => {
|
||||||
|
if (user == 1) return -1
|
||||||
|
else if (user == 2) return -2
|
||||||
|
else if (user == 4) return -3
|
||||||
|
else if (user == 3) return -4
|
||||||
|
else return 1
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { NormalConfig } from '../types/NormalConfig'
|
import { NormalConfig } from '@/types/NormalConfig'
|
||||||
import { notifyUser } from './Notifications'
|
import { notifyUser } from './Notifications'
|
||||||
|
|
||||||
export async function copyToClipboard (
|
export async function copyToClipboard (
|
||||||
File renamed without changes.
@@ -0,0 +1,50 @@
|
|||||||
|
import { message } from '@tauri-apps/plugin-dialog'
|
||||||
|
import { BaseDirectory, exists, stat } from '@tauri-apps/plugin-fs'
|
||||||
|
import { openPath } from '@tauri-apps/plugin-opener'
|
||||||
|
import { join, appLocalDataDir } from '@tauri-apps/api/path'
|
||||||
|
|
||||||
|
export const openFolder = async (name: string) => {
|
||||||
|
const relativePath = await join('game', name)
|
||||||
|
const basePath = await appLocalDataDir()
|
||||||
|
const absolutePath = await join(basePath, relativePath)
|
||||||
|
|
||||||
|
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 (folderStat.isFile) {
|
||||||
|
await message(`Game folder "${absolutePath}" not found.`, {
|
||||||
|
title: 'Folder not found',
|
||||||
|
kind: 'error'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await openPath(absolutePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatEtaSmart = (seconds: number) => {
|
||||||
|
if (seconds < 60) return `${Math.floor(seconds)}s`
|
||||||
|
if (seconds < 3600)
|
||||||
|
return `${Math.floor(seconds / 60)}m ${Math.floor(seconds % 60)}s`
|
||||||
|
if (seconds < 86400) {
|
||||||
|
const h = Math.floor(seconds / 3600)
|
||||||
|
const m = Math.floor((seconds % 3600) / 60)
|
||||||
|
return `${h}h ${m}m`
|
||||||
|
}
|
||||||
|
const d = Math.floor(seconds / 86400)
|
||||||
|
const h = Math.floor((seconds % 86400) / 3600)
|
||||||
|
return `${d}d ${h}h`
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export type BirdColor = [number, number, number]
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -2,7 +2,6 @@ export class SettingsType {
|
|||||||
constructor (
|
constructor (
|
||||||
public allowNotifications: boolean = true,
|
public allowNotifications: boolean = true,
|
||||||
public alwaysShowGamesInSidebar: boolean = true,
|
public alwaysShowGamesInSidebar: boolean = true,
|
||||||
public useLegacyInteractButtons: boolean = false,
|
|
||||||
public useWineOnUnixWhenNeeded: boolean = false,
|
public useWineOnUnixWhenNeeded: boolean = false,
|
||||||
public wineOnUnixCommand: string = 'wine %path%',
|
public wineOnUnixCommand: string = 'wine %path%',
|
||||||
public theme: number = 0
|
public theme: number = 0
|
||||||
File renamed without changes.
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2017",
|
"target": "es2024",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|||||||
Reference in new issue
Block a user