Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c69060a360
|
||
|
|
f2b0358605
|
||
|
|
57441a8872
|
||
|
|
df522122ac
|
||
|
|
f4e16c12e5
|
||
|
|
e2d98307c0
|
||
|
|
387bd54ce2
|
||
|
|
e7a61872b7
|
||
|
|
ac66c78dbf
|
||
|
|
d9302319fa
|
||
|
|
5206a7f25d
|
No files matched your search
@@ -17,6 +17,7 @@
|
|||||||
"@tauri-apps/plugin-notification": "2.3.3",
|
"@tauri-apps/plugin-notification": "2.3.3",
|
||||||
"@tauri-apps/plugin-opener": "2.5.3",
|
"@tauri-apps/plugin-opener": "2.5.3",
|
||||||
"@tauri-apps/plugin-os": "2.3.2",
|
"@tauri-apps/plugin-os": "2.3.2",
|
||||||
|
"@tauri-apps/plugin-store": "2.4.2",
|
||||||
"@tauri-apps/plugin-window-state": "2.4.1",
|
"@tauri-apps/plugin-window-state": "2.4.1",
|
||||||
"next": "16.2.1",
|
"next": "16.2.1",
|
||||||
"pretty-bytes": "7.1.0",
|
"pretty-bytes": "7.1.0",
|
||||||
@@ -280,6 +281,8 @@
|
|||||||
|
|
||||||
"@tauri-apps/plugin-os": ["@tauri-apps/plugin-os@2.3.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A=="],
|
"@tauri-apps/plugin-os": ["@tauri-apps/plugin-os@2.3.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A=="],
|
||||||
|
|
||||||
|
"@tauri-apps/plugin-store": ["@tauri-apps/plugin-store@2.4.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-0ClHS50Oq9HEvLPhNzTNFxbWVOqoAp3dRvtewQBeqfIQ0z5m3JRnOISIn2ZVPCrQC0MyGyhTS9DWhHjpigQE7A=="],
|
||||||
|
|
||||||
"@tauri-apps/plugin-window-state": ["@tauri-apps/plugin-window-state@2.4.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-OuvdrzyY8Q5Dbzpj+GcrnV1iCeoZbcFdzMjanZMMcAEUNy/6PH5pxZPXpaZLOR7whlzXiuzx0L9EKZbH7zpdRw=="],
|
"@tauri-apps/plugin-window-state": ["@tauri-apps/plugin-window-state@2.4.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-OuvdrzyY8Q5Dbzpj+GcrnV1iCeoZbcFdzMjanZMMcAEUNy/6PH5pxZPXpaZLOR7whlzXiuzx0L9EKZbH7zpdRw=="],
|
||||||
|
|
||||||
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "lncvrt-games-launcher",
|
"name": "lncvrt-games-launcher",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
"@tauri-apps/plugin-notification": "2.3.3",
|
"@tauri-apps/plugin-notification": "2.3.3",
|
||||||
"@tauri-apps/plugin-opener": "2.5.3",
|
"@tauri-apps/plugin-opener": "2.5.3",
|
||||||
"@tauri-apps/plugin-os": "2.3.2",
|
"@tauri-apps/plugin-os": "2.3.2",
|
||||||
|
"@tauri-apps/plugin-store": "2.4.2",
|
||||||
"@tauri-apps/plugin-window-state": "2.4.1",
|
"@tauri-apps/plugin-window-state": "2.4.1",
|
||||||
"next": "16.2.1",
|
"next": "16.2.1",
|
||||||
"pretty-bytes": "7.1.0",
|
"pretty-bytes": "7.1.0",
|
||||||
|
|||||||
+13
-3
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lncvrt-games-launcher"
|
name = "lncvrt-games-launcher"
|
||||||
version = "1.8.0"
|
version = "1.8.1"
|
||||||
authors = ["Lncvrt"]
|
authors = ["Lncvrt"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
@@ -25,14 +25,18 @@ tauri-plugin-fs = "2.4.5"
|
|||||||
zip = "8.4.0"
|
zip = "8.4.0"
|
||||||
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.4"
|
sha2 = "0.11.0"
|
||||||
sha2 = "0.10.9"
|
|
||||||
tauri-plugin-clipboard-manager = "2.3.2"
|
tauri-plugin-clipboard-manager = "2.3.2"
|
||||||
tauri-plugin-prevent-default = "4.0.4"
|
tauri-plugin-prevent-default = "4.0.4"
|
||||||
openssl = { version = "0.10.76", features = ["v110", "vendored"] }
|
openssl = { version = "0.10.76", features = ["v110", "vendored"] }
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
tauri-plugin-http = "2.5.7"
|
tauri-plugin-http = "2.5.7"
|
||||||
dashmap = "6.1.0"
|
dashmap = "6.1.0"
|
||||||
|
tauri-plugin-store = "2.4.2"
|
||||||
|
hex = "0.4.3"
|
||||||
|
|
||||||
|
[target.'cfg(any(target_os = "windows", target_os = "linux"))'.dependencies]
|
||||||
|
sysinfo = "0.38.4"
|
||||||
|
|
||||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||||
tauri-plugin-single-instance = "2.4.0"
|
tauri-plugin-single-instance = "2.4.0"
|
||||||
@@ -40,3 +44,9 @@ tauri-plugin-window-state = "2.4.1"
|
|||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
tauri-plugin-decorum = "1.1.1"
|
tauri-plugin-decorum = "1.1.1"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 3
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
strip = true
|
||||||
@@ -2,9 +2,7 @@
|
|||||||
"$schema": "../gen/schemas/desktop-schema.json",
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
"identifier": "default",
|
"identifier": "default",
|
||||||
"description": "Capability for the main window",
|
"description": "Capability for the main window",
|
||||||
"windows": [
|
"windows": ["main"],
|
||||||
"main"
|
|
||||||
],
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"core:default",
|
"core:default",
|
||||||
"opener:default",
|
"opener:default",
|
||||||
@@ -26,19 +24,18 @@
|
|||||||
"notification:default",
|
"notification:default",
|
||||||
"clipboard-manager:default",
|
"clipboard-manager:default",
|
||||||
"clipboard-manager:allow-write-text",
|
"clipboard-manager:allow-write-text",
|
||||||
|
"store:default",
|
||||||
|
"store:allow-load",
|
||||||
|
"store:allow-save",
|
||||||
"fs:allow-applocaldata-read",
|
"fs:allow-applocaldata-read",
|
||||||
"fs:allow-applocaldata-write",
|
"fs:allow-applocaldata-write",
|
||||||
{
|
{
|
||||||
"identifier": "fs:scope",
|
"identifier": "fs:scope",
|
||||||
"allow": [
|
"allow": [{ "path": "$APPLOCALDATA/game/**" }]
|
||||||
{ "path": "$APPLOCALDATA/game/**" }
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"identifier": "opener:allow-open-path",
|
"identifier": "opener:allow-open-path",
|
||||||
"allow": [
|
"allow": [{ "path": "$APPLOCALDATA/game/**" }]
|
||||||
{ "path": "$APPLOCALDATA/game/**" }
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"identifier": "http:default",
|
"identifier": "http:default",
|
||||||
@@ -51,7 +48,8 @@
|
|||||||
{ "url": "https://games.lncvrt.xyz/api/berrydash/leaderboard/berry" },
|
{ "url": "https://games.lncvrt.xyz/api/berrydash/leaderboard/berry" },
|
||||||
{ "url": "https://games.lncvrt.xyz/api/berrydash/leaderboard/coin" },
|
{ "url": "https://games.lncvrt.xyz/api/berrydash/leaderboard/coin" },
|
||||||
{ "url": "https://games.lncvrt.xyz/api/berrydash/leaderboard/legacy" },
|
{ "url": "https://games.lncvrt.xyz/api/berrydash/leaderboard/legacy" },
|
||||||
{ "url": "https://games.lncvrt.xyz/api/berrydash/leaderboard/total" }
|
{ "url": "https://games.lncvrt.xyz/api/berrydash/leaderboard/total" },
|
||||||
|
{ "url": "https://games.lncvrt.xyz/api/launcher/mods" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
+19
-118
@@ -15,7 +15,6 @@ use std::{
|
|||||||
process::Command,
|
process::Command,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use sysinfo::System;
|
|
||||||
use tauri::window::{ProgressBarState, ProgressBarStatus};
|
use tauri::window::{ProgressBarState, ProgressBarStatus};
|
||||||
use tauri::{AppHandle, Emitter, Manager};
|
use tauri::{AppHandle, Emitter, Manager};
|
||||||
use tauri_plugin_os::platform;
|
use tauri_plugin_os::platform;
|
||||||
@@ -25,6 +24,9 @@ use tokio::{io::AsyncWriteExt, time::timeout};
|
|||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
use zip::result::ZipError;
|
use zip::result::ZipError;
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
|
use sysinfo::System;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
@@ -38,6 +40,7 @@ fn is_cancelled(name: &str) -> bool {
|
|||||||
cancel_map().get(name).map(|v| *v).unwrap_or(false)
|
cancel_map().get(name).map(|v| *v).unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
fn is_running_by_path(path: &Path) -> bool {
|
fn is_running_by_path(path: &Path) -> bool {
|
||||||
let sys = System::new_all();
|
let sys = System::new_all();
|
||||||
let target = match path.canonicalize() {
|
let target = match path.canonicalize() {
|
||||||
@@ -151,25 +154,19 @@ async fn download(
|
|||||||
download_type: i8,
|
download_type: i8,
|
||||||
mod_id: String,
|
mod_id: String,
|
||||||
) -> String {
|
) -> String {
|
||||||
println!("[download] Starting download for '{}' from '{}'", name, url);
|
|
||||||
let _ = app.emit("download-start", format!("{}", name));
|
let _ = app.emit("download-start", format!("{}", name));
|
||||||
let window = app.get_webview_window("main").expect("main window missing");
|
let window = app.get_webview_window("main").expect("main window missing");
|
||||||
|
|
||||||
let downloads_path = match app.path().app_local_data_dir() {
|
let downloads_path = match app.path().app_local_data_dir() {
|
||||||
Ok(p) => p.join("downloads"),
|
Ok(p) => p.join("downloads"),
|
||||||
Err(e) => {
|
Err(_) => {
|
||||||
println!(
|
|
||||||
"[download] ERROR: Failed to resolve app_local_data_dir: {}",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
let _ = &app.emit("download-stop", format!("{}", name));
|
let _ = &app.emit("download-stop", format!("{}", name));
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let game_path = match app.path().app_local_data_dir() {
|
let game_path = match app.path().app_local_data_dir() {
|
||||||
Ok(p) => p.join("game"),
|
Ok(p) => p.join("game"),
|
||||||
Err(e) => {
|
Err(_) => {
|
||||||
println!("[download] ERROR: Failed to resolve game path: {}", e);
|
|
||||||
let _ = &app.emit("download-stop", format!("{}", name));
|
let _ = &app.emit("download-stop", format!("{}", name));
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
@@ -177,18 +174,12 @@ async fn download(
|
|||||||
|
|
||||||
let download_part_path = downloads_path.join(format!("{}.part", name));
|
let download_part_path = downloads_path.join(format!("{}.part", name));
|
||||||
let download_zip_path = downloads_path.join(format!("{}.zip", name));
|
let download_zip_path = downloads_path.join(format!("{}.zip", name));
|
||||||
println!("[download] Part file: {:?}", download_part_path);
|
|
||||||
println!("[download] Zip file: {:?}", download_zip_path);
|
|
||||||
|
|
||||||
let _ = tokio::fs::create_dir_all(&downloads_path).await;
|
let _ = tokio::fs::create_dir_all(&downloads_path).await;
|
||||||
|
|
||||||
let resume_from: u64 = match tokio::fs::metadata(&download_part_path).await {
|
let resume_from: u64 = match tokio::fs::metadata(&download_part_path).await {
|
||||||
Ok(meta) => {
|
Ok(meta) => {
|
||||||
let size = meta.len();
|
let size = meta.len();
|
||||||
println!(
|
|
||||||
"[download] Found existing .part file ({} bytes), will attempt to resume",
|
|
||||||
size
|
|
||||||
);
|
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
Err(_) => 0,
|
Err(_) => 0,
|
||||||
@@ -198,16 +189,11 @@ async fn download(
|
|||||||
let mut request = client.get(&url);
|
let mut request = client.get(&url);
|
||||||
if resume_from > 0 {
|
if resume_from > 0 {
|
||||||
request = request.header("Range", format!("bytes={}-", resume_from));
|
request = request.header("Range", format!("bytes={}-", resume_from));
|
||||||
println!("[download] Requesting range: bytes={}-", resume_from);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let resp = match request.send().await {
|
let resp = match request.send().await {
|
||||||
Ok(r) => {
|
Ok(r) => r,
|
||||||
println!("[download] HTTP request succeeded, status: {}", r.status());
|
Err(_) => {
|
||||||
r
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("[download] ERROR: HTTP request failed: {:?}", e);
|
|
||||||
let _ = &app.emit("download-stop", format!("{}", name));
|
let _ = &app.emit("download-stop", format!("{}", name));
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
@@ -216,19 +202,11 @@ async fn download(
|
|||||||
let status = resp.status();
|
let status = resp.status();
|
||||||
|
|
||||||
let actual_resume_from = if resume_from > 0 && status == reqwest::StatusCode::OK {
|
let actual_resume_from = if resume_from > 0 && status == reqwest::StatusCode::OK {
|
||||||
println!(
|
|
||||||
"[download] Server does not support range requests (got 200), restarting from scratch"
|
|
||||||
);
|
|
||||||
let _ = tokio::fs::remove_file(&download_part_path).await;
|
let _ = tokio::fs::remove_file(&download_part_path).await;
|
||||||
0
|
0
|
||||||
} else if resume_from > 0 && status == reqwest::StatusCode::PARTIAL_CONTENT {
|
} else if resume_from > 0 && status == reqwest::StatusCode::PARTIAL_CONTENT {
|
||||||
println!("[download] Server accepted range request (206 Partial Content)");
|
|
||||||
resume_from
|
resume_from
|
||||||
} else if resume_from > 0 {
|
} else if resume_from > 0 {
|
||||||
println!(
|
|
||||||
"[download] Unexpected status {} while resuming, restarting",
|
|
||||||
status
|
|
||||||
);
|
|
||||||
let _ = tokio::fs::remove_file(&download_part_path).await;
|
let _ = tokio::fs::remove_file(&download_part_path).await;
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
@@ -237,13 +215,6 @@ async fn download(
|
|||||||
|
|
||||||
let remaining_size = resp.content_length().unwrap_or(0);
|
let remaining_size = resp.content_length().unwrap_or(0);
|
||||||
let total_size = actual_resume_from + remaining_size;
|
let total_size = actual_resume_from + remaining_size;
|
||||||
println!(
|
|
||||||
"[download] Resume offset: {} bytes | Remaining: {} bytes | Total: {} bytes ({:.2} MB)",
|
|
||||||
actual_resume_from,
|
|
||||||
remaining_size,
|
|
||||||
total_size,
|
|
||||||
total_size as f64 / 1_048_576.0
|
|
||||||
);
|
|
||||||
let _ = app.emit("download-size", format!("{}:{}", &name, &total_size));
|
let _ = app.emit("download-size", format!("{}:{}", &name, &total_size));
|
||||||
|
|
||||||
let mut file = if actual_resume_from > 0 {
|
let mut file = if actual_resume_from > 0 {
|
||||||
@@ -252,15 +223,8 @@ async fn download(
|
|||||||
.open(&download_part_path)
|
.open(&download_part_path)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(f) => {
|
Ok(f) => f,
|
||||||
println!("[download] Opened .part file in append mode for resume");
|
Err(_) => {
|
||||||
f
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!(
|
|
||||||
"[download] ERROR: Failed to open .part file for append: {}",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
let _ = &app.emit("download-stop", format!("{}", name));
|
let _ = &app.emit("download-stop", format!("{}", name));
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
@@ -268,22 +232,14 @@ async fn download(
|
|||||||
} else {
|
} else {
|
||||||
if download_type == 0 {
|
if download_type == 0 {
|
||||||
if let Ok(true) = tokio::fs::try_exists(&game_path.join(&name)).await {
|
if let Ok(true) = tokio::fs::try_exists(&game_path.join(&name)).await {
|
||||||
println!(
|
|
||||||
"[download] Existing game dir found, removing: {:?}",
|
|
||||||
game_path.join(&name)
|
|
||||||
);
|
|
||||||
let _ = tokio::fs::remove_dir_all(&game_path.join(&name)).await;
|
let _ = tokio::fs::remove_dir_all(&game_path.join(&name)).await;
|
||||||
}
|
}
|
||||||
let _ = tokio::fs::create_dir_all(&game_path.join(&name)).await;
|
let _ = tokio::fs::create_dir_all(&game_path.join(&name)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
match tokio::fs::File::create(&download_part_path).await {
|
match tokio::fs::File::create(&download_part_path).await {
|
||||||
Ok(f) => {
|
Ok(f) => f,
|
||||||
println!("[download] Created new .part file");
|
Err(_) => {
|
||||||
f
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("[download] ERROR: Failed to create .part file: {}", e);
|
|
||||||
let _ = &app.emit("download-stop", format!("{}", name));
|
let _ = &app.emit("download-stop", format!("{}", name));
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
@@ -298,7 +254,6 @@ async fn download(
|
|||||||
|
|
||||||
while let Ok(Some(chunk_result)) = timeout(Duration::from_secs(5), stream.next()).await {
|
while let Ok(Some(chunk_result)) = timeout(Duration::from_secs(5), stream.next()).await {
|
||||||
if is_cancelled(&name) {
|
if is_cancelled(&name) {
|
||||||
println!("[download] Cancelled '{}' during chunk loop", name);
|
|
||||||
cancel_map().remove(&name);
|
cancel_map().remove(&name);
|
||||||
let _ = window.set_progress_bar(ProgressBarState {
|
let _ = window.set_progress_bar(ProgressBarState {
|
||||||
status: Some(ProgressBarStatus::None),
|
status: Some(ProgressBarStatus::None),
|
||||||
@@ -309,11 +264,7 @@ async fn download(
|
|||||||
|
|
||||||
let chunk = match chunk_result {
|
let chunk = match chunk_result {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => {
|
Err(_) => {
|
||||||
println!(
|
|
||||||
"[download] ERROR: Failed to read chunk #{}: {}",
|
|
||||||
chunk_count, e
|
|
||||||
);
|
|
||||||
let _ = &app.emit("download-stop", format!("{}", name));
|
let _ = &app.emit("download-stop", format!("{}", name));
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
@@ -321,11 +272,7 @@ async fn download(
|
|||||||
|
|
||||||
chunk_count += 1;
|
chunk_count += 1;
|
||||||
|
|
||||||
if let Err(e) = file.write_all(&chunk).await {
|
if let Err(_) = file.write_all(&chunk).await {
|
||||||
println!(
|
|
||||||
"[download] ERROR: Failed to write chunk #{} to disk: {}",
|
|
||||||
chunk_count, e
|
|
||||||
);
|
|
||||||
let _ = &app.emit("download-stop", format!("{}", name));
|
let _ = &app.emit("download-stop", format!("{}", name));
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
@@ -352,15 +299,6 @@ async fn download(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if chunk_count % 50 == 0 || downloaded == total_size {
|
if chunk_count % 50 == 0 || downloaded == total_size {
|
||||||
println!(
|
|
||||||
"[download] [{:.1}%] {:.2} MB / {:.2} MB | Speed: {:.1} KB/s | ETA: {:.1}s | Chunks: {}",
|
|
||||||
progress,
|
|
||||||
downloaded as f64 / 1_048_576.0,
|
|
||||||
total_size as f64 / 1_048_576.0,
|
|
||||||
speed / 1024.0,
|
|
||||||
eta_secs,
|
|
||||||
chunk_count,
|
|
||||||
);
|
|
||||||
let _ = window.set_progress_bar(ProgressBarState {
|
let _ = window.set_progress_bar(ProgressBarState {
|
||||||
status: Some(ProgressBarStatus::Normal),
|
status: Some(ProgressBarStatus::Normal),
|
||||||
progress: Some(progress as u64),
|
progress: Some(progress as u64),
|
||||||
@@ -377,12 +315,6 @@ async fn download(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if total_size > 0 && downloaded < total_size {
|
if total_size > 0 && downloaded < total_size {
|
||||||
println!(
|
|
||||||
"[download] ERROR: Download incomplete — got {} of {} bytes ({:.1}% complete)",
|
|
||||||
downloaded,
|
|
||||||
total_size,
|
|
||||||
(downloaded as f64 / total_size as f64) * 100.0
|
|
||||||
);
|
|
||||||
let _ = window.set_progress_bar(ProgressBarState {
|
let _ = window.set_progress_bar(ProgressBarState {
|
||||||
status: Some(ProgressBarStatus::None),
|
status: Some(ProgressBarStatus::None),
|
||||||
progress: Some(0),
|
progress: Some(0),
|
||||||
@@ -390,36 +322,21 @@ async fn download(
|
|||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
println!(
|
|
||||||
"[download] Download complete: {} bytes total in {:.2}s (this session)",
|
|
||||||
downloaded,
|
|
||||||
start.elapsed().as_secs_f64()
|
|
||||||
);
|
|
||||||
println!("[download] Verifying SHA-512 hash...");
|
|
||||||
let _ = app.emit("download-hash-checking", format!("{}", &name));
|
let _ = app.emit("download-hash-checking", format!("{}", &name));
|
||||||
|
|
||||||
let download_hash = {
|
let download_hash = {
|
||||||
let mut file = match tokio::fs::File::open(&download_part_path).await {
|
let mut file = match tokio::fs::File::open(&download_part_path).await {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(e) => {
|
Err(_) => {
|
||||||
println!(
|
|
||||||
"[download] ERROR: Failed to open .part file for hashing: {}",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut hasher = Sha512::new();
|
let mut hasher = Sha512::new();
|
||||||
let mut bytes_hashed: u64 = 0;
|
|
||||||
let mut buffer = [0u8; 8192];
|
let mut buffer = [0u8; 8192];
|
||||||
loop {
|
loop {
|
||||||
let bytes_read = match file.read(&mut buffer).await {
|
let bytes_read = match file.read(&mut buffer).await {
|
||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
Err(e) => {
|
Err(_) => {
|
||||||
println!(
|
|
||||||
"[download] ERROR: Failed to read file during hashing: {}",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -427,18 +344,12 @@ async fn download(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
hasher.update(&buffer[..bytes_read]);
|
hasher.update(&buffer[..bytes_read]);
|
||||||
bytes_hashed += bytes_read as u64;
|
|
||||||
}
|
}
|
||||||
drop(file);
|
drop(file);
|
||||||
println!("[download] Hashed {} bytes", bytes_hashed);
|
hex::encode(hasher.finalize())
|
||||||
format!("{:x}", hasher.finalize())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("[download] Expected hash: {}", hash);
|
|
||||||
println!("[download] Computed hash: {}", download_hash);
|
|
||||||
|
|
||||||
if hash != download_hash {
|
if hash != download_hash {
|
||||||
println!("[download] ERROR: Hash mismatch! Deleting corrupt .part file.");
|
|
||||||
let _ = tokio::fs::remove_file(&download_part_path).await;
|
let _ = tokio::fs::remove_file(&download_part_path).await;
|
||||||
let _ = window.set_progress_bar(ProgressBarState {
|
let _ = window.set_progress_bar(ProgressBarState {
|
||||||
status: Some(ProgressBarStatus::None),
|
status: Some(ProgressBarStatus::None),
|
||||||
@@ -447,10 +358,7 @@ async fn download(
|
|||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("[download] Hash verified OK");
|
if let Err(_) = tokio::fs::rename(&download_part_path, &download_zip_path).await {
|
||||||
println!("[download] Renaming .part -> .zip: {:?}", download_zip_path);
|
|
||||||
if let Err(e) = tokio::fs::rename(&download_part_path, &download_zip_path).await {
|
|
||||||
println!("[download] ERROR: Failed to rename .part to .zip: {}", e);
|
|
||||||
let _ = window.set_progress_bar(ProgressBarState {
|
let _ = window.set_progress_bar(ProgressBarState {
|
||||||
status: Some(ProgressBarStatus::None),
|
status: Some(ProgressBarStatus::None),
|
||||||
progress: Some(0),
|
progress: Some(0),
|
||||||
@@ -460,7 +368,6 @@ async fn download(
|
|||||||
|
|
||||||
let _ = tokio::fs::create_dir_all(&game_path.join(&name)).await;
|
let _ = tokio::fs::create_dir_all(&game_path.join(&name)).await;
|
||||||
|
|
||||||
println!("[download] Unzipping to: {:?}", game_path.join(&name));
|
|
||||||
let unzip_res = unzip_to_dir(
|
let unzip_res = unzip_to_dir(
|
||||||
app.clone(),
|
app.clone(),
|
||||||
download_zip_path.clone(),
|
download_zip_path.clone(),
|
||||||
@@ -477,11 +384,9 @@ async fn download(
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
println!("[download] Removing zip file: {:?}", download_zip_path);
|
|
||||||
let _ = tokio::fs::remove_file(&download_zip_path).await;
|
let _ = tokio::fs::remove_file(&download_zip_path).await;
|
||||||
|
|
||||||
if unzip_res == "-1" {
|
if unzip_res == "-1" {
|
||||||
println!("[download] ERROR: Unzip failed for '{}'", name);
|
|
||||||
let _ = window.set_progress_bar(ProgressBarState {
|
let _ = window.set_progress_bar(ProgressBarState {
|
||||||
status: Some(ProgressBarStatus::None),
|
status: Some(ProgressBarStatus::None),
|
||||||
progress: Some(0),
|
progress: Some(0),
|
||||||
@@ -489,10 +394,6 @@ async fn download(
|
|||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
println!(
|
|
||||||
"[download] SUCCESS: '{}' downloaded, verified, and extracted",
|
|
||||||
&name
|
|
||||||
);
|
|
||||||
let _ = window.set_progress_bar(ProgressBarState {
|
let _ = window.set_progress_bar(ProgressBarState {
|
||||||
status: Some(ProgressBarStatus::None),
|
status: Some(ProgressBarStatus::None),
|
||||||
progress: Some(0),
|
progress: Some(0),
|
||||||
@@ -612,7 +513,6 @@ fn verify_signature(body: String, signature: String, public_key: String) -> bool
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn cancel_download(name: String) {
|
async fn cancel_download(name: String) {
|
||||||
println!("[cancel_download] Cancellation requested for '{}'", name);
|
|
||||||
cancel_map().insert(name.clone(), true);
|
cancel_map().insert(name.clone(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,6 +520,7 @@ async fn cancel_download(name: String) {
|
|||||||
pub fn run() {
|
pub fn run() {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
|
.plugin(tauri_plugin_store::Builder::new().build())
|
||||||
.plugin(tauri_plugin_http::init())
|
.plugin(tauri_plugin_http::init())
|
||||||
.plugin(
|
.plugin(
|
||||||
tauri_plugin_prevent_default::Builder::new()
|
tauri_plugin_prevent_default::Builder::new()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Lncvrt Games Launcher",
|
"productName": "Lncvrt Games Launcher",
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"identifier": "xyz.lncvrt.games-launcher",
|
"identifier": "xyz.lncvrt.games-launcher",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "next dev --webpack",
|
"beforeDevCommand": "next dev --webpack",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Lncvrt Games Launcher",
|
"productName": "Lncvrt Games Launcher",
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"identifier": "xyz.lncvrt.games-launcher",
|
"identifier": "xyz.lncvrt.games-launcher",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "next dev --webpack",
|
"beforeDevCommand": "next dev --webpack",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Lncvrt Games Launcher",
|
"productName": "Lncvrt Games Launcher",
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"identifier": "xyz.lncvrt.games-launcher",
|
"identifier": "xyz.lncvrt.games-launcher",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "next dev --webpack",
|
"beforeDevCommand": "next dev --webpack",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Lncvrt Games Launcher",
|
"productName": "Lncvrt Games Launcher",
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"identifier": "xyz.lncvrt.games-launcher",
|
"identifier": "xyz.lncvrt.games-launcher",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "next dev --webpack",
|
"beforeDevCommand": "next dev --webpack",
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import {
|
|||||||
} 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 { 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 { Mod } from '@/types/Mod'
|
import { Mod } from '@/types/Mod'
|
||||||
|
import { Store } from '@tauri-apps/plugin-store'
|
||||||
|
|
||||||
type GlobalCtxType = {
|
type GlobalCtxType = {
|
||||||
serverVersionList: ServerVersionsResponse | null
|
serverVersionList: ServerVersionsResponse | null
|
||||||
@@ -29,8 +29,6 @@ type GlobalCtxType = {
|
|||||||
setFadeOut: Dispatch<SetStateAction<boolean>>
|
setFadeOut: Dispatch<SetStateAction<boolean>>
|
||||||
downloadedVersionsConfig: VersionsConfig | null
|
downloadedVersionsConfig: VersionsConfig | null
|
||||||
setDownloadedVersionsConfig: Dispatch<SetStateAction<VersionsConfig | null>>
|
setDownloadedVersionsConfig: Dispatch<SetStateAction<VersionsConfig | null>>
|
||||||
normalConfig: NormalConfig | null
|
|
||||||
setNormalConfig: Dispatch<SetStateAction<NormalConfig | null>>
|
|
||||||
managingVersion: string | null
|
managingVersion: string | null
|
||||||
setManagingVersion: Dispatch<SetStateAction<string | null>>
|
setManagingVersion: Dispatch<SetStateAction<string | null>>
|
||||||
setSelectedGame: Dispatch<SetStateAction<number | null>>
|
setSelectedGame: Dispatch<SetStateAction<number | null>>
|
||||||
@@ -63,6 +61,12 @@ type GlobalCtxType = {
|
|||||||
setViewingInfoFromDownloads: Dispatch<SetStateAction<boolean>>
|
setViewingInfoFromDownloads: Dispatch<SetStateAction<boolean>>
|
||||||
showModInfo: Mod | null
|
showModInfo: Mod | null
|
||||||
setShowModInfo: Dispatch<SetStateAction<Mod | null>>
|
setShowModInfo: Dispatch<SetStateAction<Mod | null>>
|
||||||
|
settings: Store | null
|
||||||
|
notificationsAllowed: boolean
|
||||||
|
sidebarAlwaysShowGames: boolean
|
||||||
|
linuxUseWine: boolean
|
||||||
|
linuxWineCommand: string
|
||||||
|
theme: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const GlobalCtx = createContext<GlobalCtxType | null>(null)
|
const GlobalCtx = createContext<GlobalCtxType | null>(null)
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
html {
|
||||||
|
overscroll-behavior: none;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-(--col0) text-white select-none;
|
@apply bg-(--col0) text-white select-none;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-18
@@ -21,7 +21,6 @@ export default function Installs () {
|
|||||||
setFadeOut,
|
setFadeOut,
|
||||||
setSelectedVersionList,
|
setSelectedVersionList,
|
||||||
downloadedVersionsConfig,
|
downloadedVersionsConfig,
|
||||||
normalConfig,
|
|
||||||
setManagingVersion,
|
setManagingVersion,
|
||||||
getVersionInfo,
|
getVersionInfo,
|
||||||
getGameInfo,
|
getGameInfo,
|
||||||
@@ -31,7 +30,9 @@ export default function Installs () {
|
|||||||
setCategory,
|
setCategory,
|
||||||
setDownloadedVersionsConfig,
|
setDownloadedVersionsConfig,
|
||||||
downloadVersions,
|
downloadVersions,
|
||||||
downloadProgress
|
downloadProgress,
|
||||||
|
linuxUseWine,
|
||||||
|
linuxWineCommand
|
||||||
} = useGlobal()
|
} = useGlobal()
|
||||||
|
|
||||||
const params = useSearchParams()
|
const params = useSearchParams()
|
||||||
@@ -43,7 +44,7 @@ export default function Installs () {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!showPopup) return
|
if (!showPopup) return
|
||||||
setSelectedVersionList([])
|
setSelectedVersionList([])
|
||||||
}, [normalConfig, setSelectedVersionList, showPopup])
|
}, [setSelectedVersionList, showPopup])
|
||||||
|
|
||||||
if (!id || !game) return <p>Invalid game</p>
|
if (!id || !game) return <p>Invalid game</p>
|
||||||
|
|
||||||
@@ -129,11 +130,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' && info.wine && !linuxUseWine)
|
||||||
platform() == 'linux' &&
|
|
||||||
info.wine &&
|
|
||||||
!normalConfig?.settings.useWineOnUnixWhenNeeded
|
|
||||||
)
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
return info.game === id && info.category === Number(key)
|
return info.game === id && info.category === Number(key)
|
||||||
@@ -168,8 +165,7 @@ export default function Installs () {
|
|||||||
if (
|
if (
|
||||||
platform() == 'linux' &&
|
platform() == 'linux' &&
|
||||||
info.wine &&
|
info.wine &&
|
||||||
!normalConfig?.settings
|
!linuxUseWine
|
||||||
.useWineOnUnixWhenNeeded
|
|
||||||
)
|
)
|
||||||
return false
|
return false
|
||||||
return (
|
return (
|
||||||
@@ -194,17 +190,13 @@ export default function Installs () {
|
|||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
const infoA = getVersionInfo(a)
|
const infoA = getVersionInfo(a)
|
||||||
const infoB = getVersionInfo(b)
|
const infoB = getVersionInfo(b)
|
||||||
if (!infoA || !infoB) return -1
|
if (!infoA || !infoB) return 0
|
||||||
return infoB.place - infoA.place
|
return infoB.place - infoA.place
|
||||||
})
|
})
|
||||||
.filter(v => {
|
.filter(v => {
|
||||||
const info = getVersionInfo(v)
|
const info = getVersionInfo(v)
|
||||||
if (!info) return false
|
if (!info) return false
|
||||||
if (
|
if (platform() == 'linux' && info.wine && !linuxUseWine)
|
||||||
platform() == 'linux' &&
|
|
||||||
info.wine &&
|
|
||||||
!normalConfig?.settings.useWineOnUnixWhenNeeded
|
|
||||||
)
|
|
||||||
return false
|
return false
|
||||||
return (
|
return (
|
||||||
info.game === id &&
|
info.game === id &&
|
||||||
@@ -299,9 +291,9 @@ export default function Installs () {
|
|||||||
useWine: !!(
|
useWine: !!(
|
||||||
platform() == 'linux' &&
|
platform() == 'linux' &&
|
||||||
verInfo.wine &&
|
verInfo.wine &&
|
||||||
normalConfig?.settings.useWineOnUnixWhenNeeded
|
linuxUseWine
|
||||||
),
|
),
|
||||||
wineCommand: normalConfig?.settings.wineOnUnixCommand
|
wineCommand: linuxWineCommand
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
onContextMenu={e => {
|
onContextMenu={e => {
|
||||||
|
|||||||
+169
-43
@@ -2,18 +2,13 @@
|
|||||||
|
|
||||||
import './Globals.css'
|
import './Globals.css'
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import Sidebar from '@/componets/Sidebar'
|
import Sidebar from '@/components/Sidebar'
|
||||||
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 { faChevronLeft, faXmark } from '@fortawesome/free-solid-svg-icons'
|
import { faChevronLeft, faXmark } from '@fortawesome/free-solid-svg-icons'
|
||||||
import {
|
import { readVersionsConfig, writeVersionsConfig } from '@/lib/BazookaManager'
|
||||||
readNormalConfig,
|
|
||||||
readVersionsConfig,
|
|
||||||
writeVersionsConfig
|
|
||||||
} from '@/lib/BazookaManager'
|
|
||||||
import { VersionsConfig } from '@/types/VersionsConfig'
|
import { VersionsConfig } from '@/types/VersionsConfig'
|
||||||
import { NormalConfig } from '@/types/NormalConfig'
|
|
||||||
import { app } from '@tauri-apps/api'
|
import { app } from '@tauri-apps/api'
|
||||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||||
import { GlobalProvider } from './GlobalProvider'
|
import { GlobalProvider } from './GlobalProvider'
|
||||||
@@ -24,22 +19,28 @@ 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 { notifyUser } from '@/lib/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,
|
||||||
|
readTextFile,
|
||||||
|
remove
|
||||||
|
} from '@tauri-apps/plugin-fs'
|
||||||
import { fetch } from '@tauri-apps/plugin-http'
|
import { fetch } from '@tauri-apps/plugin-http'
|
||||||
import { verifySignature } from '@/lib/Util'
|
import { verifySignature } from '@/lib/Util'
|
||||||
import { getCurrentWindow, UserAttentionType } from '@tauri-apps/api/window'
|
import { getCurrentWindow, UserAttentionType } from '@tauri-apps/api/window'
|
||||||
|
|
||||||
import VersionsDownloadPopup from '@/componets/popups/VersionsDownload'
|
import VersionsDownloadPopup from '@/components/popups/VersionsDownload'
|
||||||
import GamesDownloadPopup from '@/componets/popups/GamesDownload'
|
import GamesDownloadPopup from '@/components/popups/GamesDownload'
|
||||||
import DownloadsPopup from '@/componets/popups/Downloads'
|
import DownloadsPopup from '@/components/popups/Downloads'
|
||||||
import ManageVersionPopup from '@/componets/popups/ManageVersion'
|
import ManageVersionPopup from '@/components/popups/ManageVersion'
|
||||||
import ModDownloadsPopup from '@/componets/popups/ModDownloads'
|
import ModDownloadsPopup from '@/components/popups/ModDownloads'
|
||||||
import { Mod } from '@/types/Mod'
|
import { Mod } from '@/types/Mod'
|
||||||
|
import { load, Store } from '@tauri-apps/plugin-store'
|
||||||
|
import { notifyUser } from '@/lib/Notifications'
|
||||||
|
|
||||||
const roboto = Roboto({
|
const roboto = Roboto({
|
||||||
subsets: ['latin']
|
subsets: ['latin']
|
||||||
@@ -62,7 +63,6 @@ export default function RootLayout ({
|
|||||||
|
|
||||||
const [downloadedVersionsConfig, setDownloadedVersionsConfig] =
|
const [downloadedVersionsConfig, setDownloadedVersionsConfig] =
|
||||||
useState<VersionsConfig | null>(null)
|
useState<VersionsConfig | null>(null)
|
||||||
const [normalConfig, setNormalConfig] = useState<NormalConfig | null>(null)
|
|
||||||
|
|
||||||
const [showPopup, setShowPopup] = useState(false)
|
const [showPopup, setShowPopup] = useState(false)
|
||||||
const [popupMode, setPopupMode] = useState<null | number>(null)
|
const [popupMode, setPopupMode] = useState<null | number>(null)
|
||||||
@@ -84,20 +84,26 @@ export default function RootLayout ({
|
|||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const revisionCheck = useRef(false)
|
const revisionCheck = useRef(false)
|
||||||
|
|
||||||
|
const [settings, setSettings] = useState<Store | null>(null)
|
||||||
|
|
||||||
|
const [notificationsAllowed, setNotificationsAllowed] =
|
||||||
|
useState<boolean>(false)
|
||||||
|
const [sidebarAlwaysShowGames, setSidebarAlwaysShowGames] =
|
||||||
|
useState<boolean>(false)
|
||||||
|
const [linuxUseWine, setLinuxUseWine] = useState<boolean>(false)
|
||||||
|
const [linuxWineCommand, setLinuxWineCommand] =
|
||||||
|
useState<string>('wine %path%')
|
||||||
|
const [theme, setTheme] = useState<string>('dark')
|
||||||
|
|
||||||
function getSpecialVersionsList (game?: number): GameVersion[] {
|
function getSpecialVersionsList (game?: number): GameVersion[] {
|
||||||
if (!normalConfig || !serverVersionList) return []
|
if (!serverVersionList) return []
|
||||||
|
|
||||||
return serverVersionList.versions
|
return serverVersionList.versions
|
||||||
.filter(
|
.filter(
|
||||||
v => !Object.keys(downloadedVersionsConfig?.list ?? []).includes(v.id)
|
v => !Object.keys(downloadedVersionsConfig?.list ?? []).includes(v.id)
|
||||||
)
|
)
|
||||||
.filter(v => {
|
.filter(v => {
|
||||||
if (
|
if (platformName == 'linux' && v.wine && linuxUseWine) return false
|
||||||
platformName == 'linux' &&
|
|
||||||
v.wine &&
|
|
||||||
!normalConfig.settings.useWineOnUnixWhenNeeded
|
|
||||||
)
|
|
||||||
return false
|
|
||||||
|
|
||||||
if (game && v.game != game) return false
|
if (game && v.game != game) return false
|
||||||
if (category != -1 && v.category != category) return false
|
if (category != -1 && v.category != category) return false
|
||||||
@@ -150,8 +156,7 @@ export default function RootLayout ({
|
|||||||
} | null {
|
} | null {
|
||||||
if (!downloadedVersionsConfig || !serverVersionList) return null
|
if (!downloadedVersionsConfig || !serverVersionList) return null
|
||||||
|
|
||||||
const allowWine =
|
const allowWine = platformName !== 'linux' || linuxUseWine
|
||||||
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)
|
||||||
@@ -353,10 +358,85 @@ export default function RootLayout ({
|
|||||||
setLoadingText('Failed to download versions list.')
|
setLoadingText('Failed to download versions list.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const normalConfig = await readNormalConfig()
|
const settingsLocal = await load('settings.json', {
|
||||||
|
autoSave: true,
|
||||||
|
defaults: {
|
||||||
|
version: client,
|
||||||
|
notificationsAllowed: true,
|
||||||
|
sidebarAlwaysShowGames: true,
|
||||||
|
linuxUseWine: false,
|
||||||
|
linuxWineCommand: 'wine %path%',
|
||||||
|
theme: 'dark'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
settingsLocal.set('version', client)
|
||||||
|
const oldSettingsOptions = {
|
||||||
|
baseDir: BaseDirectory.AppLocalData
|
||||||
|
}
|
||||||
|
if (await exists('config.json', oldSettingsOptions)) {
|
||||||
|
const config = await readTextFile('config.json', oldSettingsOptions)
|
||||||
|
const raw = JSON.parse(config)
|
||||||
|
if (
|
||||||
|
raw.settings &&
|
||||||
|
raw.settings.theme &&
|
||||||
|
(raw.version == '1.0.0' ||
|
||||||
|
raw.version == '1.1.0' ||
|
||||||
|
raw.version == '1.1.1' ||
|
||||||
|
raw.version == '1.2.0' ||
|
||||||
|
raw.version == '1.3.0' ||
|
||||||
|
raw.version == '1.3.1' ||
|
||||||
|
raw.version == '1.4.0' ||
|
||||||
|
raw.version == '1.5.0' ||
|
||||||
|
raw.version == '1.5.1' ||
|
||||||
|
raw.version == '1.5.2' ||
|
||||||
|
raw.version == '1.5.3' ||
|
||||||
|
raw.version == '1.5.4')
|
||||||
|
) {
|
||||||
|
const parsed = Number(raw.settings.theme)
|
||||||
|
if (parsed == 3) raw.settings.theme = 2
|
||||||
|
if (parsed == 4) raw.settings.theme = 3
|
||||||
|
else if (parsed != 0 && parsed != 1) raw.settings.theme = 0
|
||||||
|
}
|
||||||
|
if (raw.settings.allowNotifications)
|
||||||
|
settingsLocal?.set(
|
||||||
|
'notificationsAllowed',
|
||||||
|
Boolean(raw.settings.allowNotifications)
|
||||||
|
)
|
||||||
|
if (raw.settings.alwaysShowGamesInSidebar)
|
||||||
|
settingsLocal?.set(
|
||||||
|
'sidebarAlwaysShowGames',
|
||||||
|
Boolean(raw.settings.alwaysShowGamesInSidebar)
|
||||||
|
)
|
||||||
|
if (raw.settings.useWineOnUnixWhenNeeded)
|
||||||
|
settingsLocal?.set(
|
||||||
|
'linuxUseWine',
|
||||||
|
Boolean(raw.settings.useWineOnUnixWhenNeeded)
|
||||||
|
)
|
||||||
|
if (raw.settings.wineOnUnixCommand)
|
||||||
|
settingsLocal?.set(
|
||||||
|
'linuxWineCommand',
|
||||||
|
String(raw.settings.wineOnUnixCommand)
|
||||||
|
)
|
||||||
|
if (raw.settings.theme) {
|
||||||
|
const tempTheme = (() => {
|
||||||
|
switch (Number(raw.settings.theme)) {
|
||||||
|
case 1:
|
||||||
|
return 'red'
|
||||||
|
case 2:
|
||||||
|
return 'blue'
|
||||||
|
case 3:
|
||||||
|
return 'purple'
|
||||||
|
default:
|
||||||
|
return 'dark'
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
settingsLocal?.set('theme', tempTheme)
|
||||||
|
}
|
||||||
|
await remove('config.json', oldSettingsOptions)
|
||||||
|
}
|
||||||
const versionsConfig = await readVersionsConfig()
|
const versionsConfig = await readVersionsConfig()
|
||||||
setDownloadedVersionsConfig(versionsConfig)
|
setDownloadedVersionsConfig(versionsConfig)
|
||||||
setNormalConfig(normalConfig)
|
setSettings(settingsLocal)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|
||||||
if (!(await isPermissionGranted())) {
|
if (!(await isPermissionGranted())) {
|
||||||
@@ -475,7 +555,7 @@ export default function RootLayout ({
|
|||||||
: d
|
: d
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if (normalConfig?.settings.allowNotifications)
|
if (await settings?.get<boolean>('notifications-allowed'))
|
||||||
await notifyUser(
|
await notifyUser(
|
||||||
'Download Failed',
|
'Download Failed',
|
||||||
`The download for version ${info.displayName} has failed.`
|
`The download for version ${info.displayName} has failed.`
|
||||||
@@ -560,7 +640,7 @@ export default function RootLayout ({
|
|||||||
: d
|
: d
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if (normalConfig?.settings.allowNotifications)
|
if (await settings?.get<boolean>('notifications-allowed'))
|
||||||
await notifyUser(
|
await notifyUser(
|
||||||
'Download Failed',
|
'Download Failed',
|
||||||
`The download for version ${info.displayName} has failed.`
|
`The download for version ${info.displayName} has failed.`
|
||||||
@@ -581,7 +661,7 @@ export default function RootLayout ({
|
|||||||
getVersionInfo,
|
getVersionInfo,
|
||||||
getGameInfo,
|
getGameInfo,
|
||||||
downloadProgress,
|
downloadProgress,
|
||||||
normalConfig
|
settings
|
||||||
])
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -612,20 +692,62 @@ export default function RootLayout ({
|
|||||||
})()
|
})()
|
||||||
}, [serverVersionList, downloadedVersionsConfig, downloadVersions])
|
}, [serverVersionList, downloadedVersionsConfig, downloadVersions])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
downloadProgress.length === 0 &&
|
||||||
|
downloadQueue.length === 0 &&
|
||||||
|
showPopup &&
|
||||||
|
popupMode === 1
|
||||||
|
) {
|
||||||
|
setTimeout(async () => {
|
||||||
|
if (notificationsAllowed) {
|
||||||
|
await notifyUser('Downloads Complete', 'All downloads finished!')
|
||||||
|
}
|
||||||
|
await getCurrentWindow().requestUserAttention(
|
||||||
|
UserAttentionType.Informational
|
||||||
|
)
|
||||||
|
closePopup()
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
downloadProgress,
|
||||||
|
downloadQueue,
|
||||||
|
showPopup,
|
||||||
|
popupMode,
|
||||||
|
closePopup,
|
||||||
|
notificationsAllowed
|
||||||
|
])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!settings) return
|
||||||
|
const unlisteners: Promise<() => void>[] = []
|
||||||
|
|
||||||
|
function watch<T> (key: string, fn: (v: T | undefined) => void) {
|
||||||
|
settings!.get<T>(key).then(fn)
|
||||||
|
unlisteners.push(settings!.onKeyChange<T>(key, fn))
|
||||||
|
}
|
||||||
|
|
||||||
|
watch<string>('theme', v => setTheme(v ?? 'dark'))
|
||||||
|
watch<boolean>('notificationsAllowed', v =>
|
||||||
|
setNotificationsAllowed(v ?? true)
|
||||||
|
)
|
||||||
|
watch<boolean>('sidebarAlwaysShowGames', v =>
|
||||||
|
setSidebarAlwaysShowGames(v ?? true)
|
||||||
|
)
|
||||||
|
watch<boolean>('linuxUseWine', v => setLinuxUseWine(v ?? false))
|
||||||
|
watch<string>('linuxWineCommand', v =>
|
||||||
|
setLinuxWineCommand(v ?? 'wine %path%')
|
||||||
|
)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unlisteners.forEach(u => u.then(fn => fn()))
|
||||||
|
}
|
||||||
|
}, [settings])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<html lang='en' className={roboto.className}>
|
<html lang='en' className={roboto.className}>
|
||||||
<body
|
<body className={theme + '-theme'}>
|
||||||
className={
|
|
||||||
normalConfig?.settings.theme === 1
|
|
||||||
? 'red-theme'
|
|
||||||
: normalConfig?.settings.theme === 2
|
|
||||||
? 'blue-theme'
|
|
||||||
: normalConfig?.settings.theme === 3
|
|
||||||
? 'purple-theme'
|
|
||||||
: 'dark-theme'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
@@ -675,8 +797,6 @@ export default function RootLayout ({
|
|||||||
setFadeOut,
|
setFadeOut,
|
||||||
downloadedVersionsConfig,
|
downloadedVersionsConfig,
|
||||||
setDownloadedVersionsConfig,
|
setDownloadedVersionsConfig,
|
||||||
normalConfig,
|
|
||||||
setNormalConfig,
|
|
||||||
managingVersion,
|
managingVersion,
|
||||||
setManagingVersion,
|
setManagingVersion,
|
||||||
getVersionInfo,
|
getVersionInfo,
|
||||||
@@ -696,7 +816,13 @@ export default function RootLayout ({
|
|||||||
selectedGame,
|
selectedGame,
|
||||||
setViewingInfoFromDownloads,
|
setViewingInfoFromDownloads,
|
||||||
showModInfo,
|
showModInfo,
|
||||||
setShowModInfo
|
setShowModInfo,
|
||||||
|
settings,
|
||||||
|
notificationsAllowed,
|
||||||
|
sidebarAlwaysShowGames,
|
||||||
|
linuxUseWine,
|
||||||
|
linuxWineCommand,
|
||||||
|
theme
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
+1
-2
@@ -20,7 +20,6 @@ export default function Installs () {
|
|||||||
setFadeOut,
|
setFadeOut,
|
||||||
setSelectedVersionList,
|
setSelectedVersionList,
|
||||||
downloadedVersionsConfig,
|
downloadedVersionsConfig,
|
||||||
normalConfig,
|
|
||||||
setSelectedGame,
|
setSelectedGame,
|
||||||
getListOfGames,
|
getListOfGames,
|
||||||
getVersionsAmountData,
|
getVersionsAmountData,
|
||||||
@@ -32,7 +31,7 @@ export default function Installs () {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!showPopup) return
|
if (!showPopup) return
|
||||||
setSelectedVersionList([])
|
setSelectedVersionList([])
|
||||||
}, [normalConfig, setSelectedVersionList, showPopup])
|
}, [setSelectedVersionList, showPopup])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mx-4 mt-4'>
|
<div className='mx-4 mt-4'>
|
||||||
|
|||||||
+65
-162
@@ -1,181 +1,84 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useEffect, useState } from 'react'
|
import { Setting } from '@/components/Setting'
|
||||||
import { Setting } from '@/componets/Setting'
|
|
||||||
import { writeNormalConfig } from '@/lib/BazookaManager'
|
|
||||||
import { useGlobal } from '@/app/GlobalProvider'
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
import { copyToClipboard } from '@/lib/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 {
|
||||||
const [alwaysShowGamesInSidebar, setAlwaysShowGamesInSidebar] =
|
settings,
|
||||||
useState(false)
|
version,
|
||||||
const [useWineOnUnixWhenNeeded, setUseWineOnUnixWhenNeeded] = useState(false)
|
notificationsAllowed,
|
||||||
const [wineOnUnixCommand, setWineOnUnixCommand] = useState('wine %path%')
|
sidebarAlwaysShowGames,
|
||||||
const [theme, setTheme] = useState(0)
|
linuxUseWine,
|
||||||
|
linuxWineCommand,
|
||||||
const [loaded, setLoaded] = useState(false)
|
theme
|
||||||
const { normalConfig, setNormalConfig, version } = useGlobal()
|
} = useGlobal()
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
;(async () => {
|
|
||||||
while (normalConfig != null) {
|
|
||||||
setAllowNotifications(normalConfig.settings.allowNotifications)
|
|
||||||
setAlwaysShowGamesInSidebar(
|
|
||||||
normalConfig.settings.alwaysShowGamesInSidebar
|
|
||||||
)
|
|
||||||
setUseWineOnUnixWhenNeeded(
|
|
||||||
normalConfig.settings.useWineOnUnixWhenNeeded
|
|
||||||
)
|
|
||||||
setWineOnUnixCommand(normalConfig.settings.wineOnUnixCommand)
|
|
||||||
setTheme(normalConfig.settings.theme)
|
|
||||||
setLoaded(true)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
}, [normalConfig])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p className='text-3xl ml-4 mt-4'>Settings</p>
|
<p className='text-3xl ml-4 mt-4'>Settings</p>
|
||||||
{loaded && (
|
<div className='ml-4 mt-4 bg-(--col1) border border-(--col3) rounded-lg p-4 w-fit h-fit'>
|
||||||
<div className='ml-4 mt-4 bg-(--col1) border border-(--col3) rounded-lg p-4 w-fit h-fit'>
|
<Setting
|
||||||
<Setting
|
label='Allow sending notifications'
|
||||||
label='Allow sending notifications'
|
value={notificationsAllowed}
|
||||||
value={allowNotifications}
|
onChange={async () => {
|
||||||
onChange={async () => {
|
await settings?.set('notificationsAllowed', !notificationsAllowed)
|
||||||
if (!normalConfig) return
|
}}
|
||||||
setAllowNotifications(!allowNotifications)
|
title='This setting does as you expect, allow the launcher to send notifications for when stuff like downloading is done.'
|
||||||
setNormalConfig({
|
/>
|
||||||
...normalConfig,
|
<Setting
|
||||||
settings: {
|
label='Always show games in sidebar'
|
||||||
...normalConfig.settings,
|
value={sidebarAlwaysShowGames}
|
||||||
allowNotifications: !allowNotifications
|
onChange={async () => {
|
||||||
}
|
await settings?.set(
|
||||||
})
|
'sidebarAlwaysShowGames',
|
||||||
await writeNormalConfig({
|
!sidebarAlwaysShowGames
|
||||||
...normalConfig,
|
)
|
||||||
settings: {
|
}}
|
||||||
...normalConfig.settings,
|
title="This setting will make it so when you are on a page like this, the games won't disappear."
|
||||||
allowNotifications: !allowNotifications
|
/>
|
||||||
}
|
<Setting
|
||||||
})
|
label='Use wine if needed'
|
||||||
}}
|
value={linuxUseWine}
|
||||||
title='This setting does as you expect, allow the launcher to send notifications for when stuff like downloading is done.'
|
onChange={async () => {
|
||||||
/>
|
await settings?.set('linuxUseWine', !linuxUseWine)
|
||||||
<Setting
|
}}
|
||||||
label='Always show games in sidebar'
|
className={platform() == 'linux' ? '' : 'hidden'}
|
||||||
value={alwaysShowGamesInSidebar}
|
/>
|
||||||
onChange={async () => {
|
<p hidden={!(platform() == 'linux' && linuxUseWine)}>Wine Command:</p>
|
||||||
if (!normalConfig) return
|
<input
|
||||||
setAlwaysShowGamesInSidebar(!alwaysShowGamesInSidebar)
|
type='text'
|
||||||
setNormalConfig({
|
value={linuxWineCommand}
|
||||||
...normalConfig,
|
onChange={async e => {
|
||||||
settings: {
|
await settings?.set('linuxWineCommand', e.target.value)
|
||||||
...normalConfig.settings,
|
}}
|
||||||
alwaysShowGamesInSidebar: !alwaysShowGamesInSidebar
|
className={`input-field my-1 ${
|
||||||
}
|
platform() == 'linux' && linuxWineCommand ? '' : 'hidden'
|
||||||
})
|
}`}
|
||||||
await writeNormalConfig({
|
></input>
|
||||||
...normalConfig,
|
<div title='The theme you want the launcher to use.'>
|
||||||
settings: {
|
<label className='text-lg'>Theme:</label>
|
||||||
...normalConfig.settings,
|
<select
|
||||||
alwaysShowGamesInSidebar: !alwaysShowGamesInSidebar
|
className='ml-2 bg-(--col2) border border-(--col4) rounded-md'
|
||||||
}
|
value={theme}
|
||||||
})
|
|
||||||
}}
|
|
||||||
title="This setting will make it so when you are on a page like this, the games won't disappear."
|
|
||||||
/>
|
|
||||||
<Setting
|
|
||||||
label='Use wine when needed to launch games'
|
|
||||||
value={useWineOnUnixWhenNeeded}
|
|
||||||
onChange={async () => {
|
|
||||||
if (!normalConfig) return
|
|
||||||
setUseWineOnUnixWhenNeeded(!useWineOnUnixWhenNeeded)
|
|
||||||
setNormalConfig({
|
|
||||||
...normalConfig,
|
|
||||||
settings: {
|
|
||||||
...normalConfig.settings,
|
|
||||||
useWineOnUnixWhenNeeded: !useWineOnUnixWhenNeeded
|
|
||||||
}
|
|
||||||
})
|
|
||||||
await writeNormalConfig({
|
|
||||||
...normalConfig,
|
|
||||||
settings: {
|
|
||||||
...normalConfig.settings,
|
|
||||||
useWineOnUnixWhenNeeded: !useWineOnUnixWhenNeeded
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
className={platform() == 'linux' ? '' : 'hidden'}
|
|
||||||
/>
|
|
||||||
<p hidden={!(platform() == 'linux' && useWineOnUnixWhenNeeded)}>
|
|
||||||
Wine Command:
|
|
||||||
</p>
|
|
||||||
<input
|
|
||||||
type='text'
|
|
||||||
value={wineOnUnixCommand}
|
|
||||||
onChange={async e => {
|
onChange={async e => {
|
||||||
if (!normalConfig) return
|
await settings?.set('theme', e.target.value)
|
||||||
setWineOnUnixCommand(e.target.value)
|
|
||||||
setNormalConfig({
|
|
||||||
...normalConfig,
|
|
||||||
settings: {
|
|
||||||
...normalConfig.settings,
|
|
||||||
wineOnUnixCommand: e.target.value
|
|
||||||
}
|
|
||||||
})
|
|
||||||
await writeNormalConfig({
|
|
||||||
...normalConfig,
|
|
||||||
settings: {
|
|
||||||
...normalConfig.settings,
|
|
||||||
wineOnUnixCommand: e.target.value
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
}}
|
||||||
className={`input-field my-1 ${
|
>
|
||||||
platform() == 'linux' && useWineOnUnixWhenNeeded ? '' : 'hidden'
|
<option value={'dark'}>Dark (default)</option>
|
||||||
}`}
|
<option value={'red'}>Red</option>
|
||||||
></input>
|
<option value={'blue'}>Blue</option>
|
||||||
<div title='The theme you want the launcher to use.'>
|
<option value={'purple'}>Purple</option>
|
||||||
<label className='text-lg'>Theme:</label>
|
</select>
|
||||||
<select
|
|
||||||
className='ml-2 bg-(--col2) border border-(--col4) rounded-md'
|
|
||||||
value={theme}
|
|
||||||
onChange={async e => {
|
|
||||||
if (!normalConfig) return
|
|
||||||
const newTheme = parseInt(e.target.value)
|
|
||||||
setTheme(newTheme)
|
|
||||||
setNormalConfig({
|
|
||||||
...normalConfig,
|
|
||||||
settings: {
|
|
||||||
...normalConfig.settings,
|
|
||||||
theme: newTheme
|
|
||||||
}
|
|
||||||
})
|
|
||||||
await writeNormalConfig({
|
|
||||||
...normalConfig,
|
|
||||||
settings: {
|
|
||||||
...normalConfig.settings,
|
|
||||||
theme: newTheme
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<option value={0}>Dark (default)</option>
|
|
||||||
<option value={1}>Red</option>
|
|
||||||
<option value={2}>Blue</option>
|
|
||||||
<option value={3}>Purple</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
<p
|
<p
|
||||||
className='fixed bottom-1.5 right-1.5 rounded-md cursor-pointer px-1 border z-100 transition-colors btntheme1'
|
className='fixed bottom-1.5 right-1.5 rounded-md cursor-pointer px-1 border z-100 transition-colors btntheme1'
|
||||||
onClick={async () => {
|
onClick={async () =>
|
||||||
await copyToClipboard(`v${version}`, normalConfig)
|
await copyToClipboard(`v${version}`, notificationsAllowed)
|
||||||
}}
|
}
|
||||||
title='The current launcher version.'
|
title='The current launcher version.'
|
||||||
>
|
>
|
||||||
v{version}
|
v{version}
|
||||||
|
|||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -27,7 +27,6 @@ const lexend = Lexend({
|
|||||||
|
|
||||||
export default function Sidebar () {
|
export default function Sidebar () {
|
||||||
const {
|
const {
|
||||||
normalConfig,
|
|
||||||
getListOfGames,
|
getListOfGames,
|
||||||
setShowPopup,
|
setShowPopup,
|
||||||
setPopupMode,
|
setPopupMode,
|
||||||
@@ -36,7 +35,9 @@ export default function Sidebar () {
|
|||||||
downloadedVersionsConfig,
|
downloadedVersionsConfig,
|
||||||
getVersionInfo,
|
getVersionInfo,
|
||||||
category,
|
category,
|
||||||
setCategory
|
setCategory,
|
||||||
|
sidebarAlwaysShowGames,
|
||||||
|
linuxUseWine
|
||||||
} = useGlobal()
|
} = useGlobal()
|
||||||
|
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
@@ -104,7 +105,7 @@ export default function Sidebar () {
|
|||||||
? 'active'
|
? 'active'
|
||||||
: ''
|
: ''
|
||||||
} ml-auto w-50 ${
|
} ml-auto w-50 ${
|
||||||
normalConfig?.settings.alwaysShowGamesInSidebar ||
|
sidebarAlwaysShowGames ||
|
||||||
pathname === '/' ||
|
pathname === '/' ||
|
||||||
pathname.startsWith('/game')
|
pathname.startsWith('/game')
|
||||||
? ''
|
? ''
|
||||||
@@ -136,11 +137,7 @@ export default function Sidebar () {
|
|||||||
const info = getVersionInfo(v)
|
const info = getVersionInfo(v)
|
||||||
if (!info) return false
|
if (!info) return false
|
||||||
|
|
||||||
if (
|
if (platform() == 'linux' && info.wine && !linuxUseWine)
|
||||||
platform() == 'linux' &&
|
|
||||||
info.wine &&
|
|
||||||
!normalConfig?.settings.useWineOnUnixWhenNeeded
|
|
||||||
)
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
return info.game === i.id && info.category === Number(key)
|
return info.game === i.id && info.category === Number(key)
|
||||||
@@ -161,7 +158,7 @@ export default function Sidebar () {
|
|||||||
? 'active'
|
? 'active'
|
||||||
: ''
|
: ''
|
||||||
} ml-auto w-47.5 ${
|
} ml-auto w-47.5 ${
|
||||||
normalConfig?.settings.alwaysShowGamesInSidebar ||
|
sidebarAlwaysShowGames ||
|
||||||
pathname === '/' ||
|
pathname === '/' ||
|
||||||
pathname.startsWith('/game')
|
pathname.startsWith('/game')
|
||||||
? ''
|
? ''
|
||||||
@@ -21,8 +21,8 @@ export default function DownloadsPopup () {
|
|||||||
setDownloadProgress,
|
setDownloadProgress,
|
||||||
downloadQueue,
|
downloadQueue,
|
||||||
setDownloadQueue,
|
setDownloadQueue,
|
||||||
normalConfig,
|
setDownloadedVersionsConfig,
|
||||||
setDownloadedVersionsConfig
|
notificationsAllowed
|
||||||
} = useGlobal()
|
} = useGlobal()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -58,28 +58,42 @@ export default function DownloadsPopup () {
|
|||||||
const res = await invoke<string>('download', {
|
const res = await invoke<string>('download', {
|
||||||
url: v.url,
|
url: v.url,
|
||||||
name: v.version,
|
name: v.version,
|
||||||
executable: v.executable,
|
hash: v.hash,
|
||||||
hash: v.hash
|
downloadType: v.type,
|
||||||
|
modId: String(v.modId ?? '')
|
||||||
})
|
})
|
||||||
|
|
||||||
if (res === '1') {
|
if (res === '1') {
|
||||||
setDownloadProgress(prev =>
|
setDownloadProgress(prev =>
|
||||||
prev.filter(d => d.version !== v.version)
|
prev.filter(d => d.version !== v.version)
|
||||||
)
|
)
|
||||||
setDownloadedVersionsConfig(prev => {
|
if (v.type != 1) {
|
||||||
if (!prev) return prev
|
setDownloadedVersionsConfig(prev => {
|
||||||
|
if (!prev) return prev
|
||||||
|
|
||||||
const updated = {
|
const updated =
|
||||||
...prev,
|
v.type == 2
|
||||||
list: {
|
? {
|
||||||
...prev.list,
|
...prev,
|
||||||
[v.version]: Date.now()
|
mods: {
|
||||||
}
|
...prev.mods,
|
||||||
}
|
[v.modGame! + '-' + v.modId!]: {
|
||||||
|
[v.modVersion!]: Date.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...prev,
|
||||||
|
list: {
|
||||||
|
...prev.list,
|
||||||
|
[v.version]: Date.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
writeVersionsConfig(updated)
|
writeVersionsConfig(updated)
|
||||||
return updated
|
return updated
|
||||||
})
|
})
|
||||||
|
}
|
||||||
} else if (res == '0') {
|
} else if (res == '0') {
|
||||||
setDownloadProgress(prev => {
|
setDownloadProgress(prev => {
|
||||||
const i = prev.findIndex(d => d.version === v.version)
|
const i = prev.findIndex(d => d.version === v.version)
|
||||||
@@ -109,7 +123,7 @@ export default function DownloadsPopup () {
|
|||||||
: d
|
: d
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if (normalConfig?.settings.allowNotifications)
|
if (notificationsAllowed)
|
||||||
await notifyUser(
|
await notifyUser(
|
||||||
'Download Failed',
|
'Download Failed',
|
||||||
`The download for version ${
|
`The download for version ${
|
||||||
File renamed without changes.
File renamed without changes.
@@ -17,6 +17,7 @@ import {
|
|||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { message } from '@tauri-apps/plugin-dialog'
|
import { message } from '@tauri-apps/plugin-dialog'
|
||||||
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
|
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
|
||||||
|
import { fetch } from '@tauri-apps/plugin-http'
|
||||||
import { arch, platform } from '@tauri-apps/plugin-os'
|
import { arch, platform } from '@tauri-apps/plugin-os'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
File renamed without changes.
@@ -1,5 +1,4 @@
|
|||||||
import { app } from '@tauri-apps/api'
|
import { app } from '@tauri-apps/api'
|
||||||
import { NormalConfig } from '@/types/NormalConfig'
|
|
||||||
import {
|
import {
|
||||||
BaseDirectory,
|
BaseDirectory,
|
||||||
create,
|
create,
|
||||||
@@ -10,80 +9,6 @@ import {
|
|||||||
} 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> {
|
|
||||||
const version = await app.getVersion()
|
|
||||||
try {
|
|
||||||
const options = {
|
|
||||||
baseDir: BaseDirectory.AppLocalData
|
|
||||||
}
|
|
||||||
const doesFolderExist = await exists('', options)
|
|
||||||
const doesConfigExist = await exists('config.json', options)
|
|
||||||
if (!doesFolderExist || !doesConfigExist) {
|
|
||||||
if (!doesFolderExist) {
|
|
||||||
await mkdir('', options)
|
|
||||||
}
|
|
||||||
const file = await create('config.json', options)
|
|
||||||
await file.write(
|
|
||||||
new TextEncoder().encode(
|
|
||||||
JSON.stringify(new NormalConfig(version), null, 2)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
await file.close()
|
|
||||||
return new NormalConfig(version)
|
|
||||||
}
|
|
||||||
const config = await readTextFile('config.json', options)
|
|
||||||
const raw = JSON.parse(config)
|
|
||||||
if (
|
|
||||||
raw.settings &&
|
|
||||||
raw.settings.theme &&
|
|
||||||
(raw.version == '1.0.0' ||
|
|
||||||
raw.version == '1.1.0' ||
|
|
||||||
raw.version == '1.1.1' ||
|
|
||||||
raw.version == '1.2.0' ||
|
|
||||||
raw.version == '1.3.0' ||
|
|
||||||
raw.version == '1.3.1' ||
|
|
||||||
raw.version == '1.4.0' ||
|
|
||||||
raw.version == '1.5.0' ||
|
|
||||||
raw.version == '1.5.1' ||
|
|
||||||
raw.version == '1.5.2' ||
|
|
||||||
raw.version == '1.5.3' ||
|
|
||||||
raw.version == '1.5.4')
|
|
||||||
) {
|
|
||||||
const parsed = Number(raw.settings.theme)
|
|
||||||
if (parsed == 3) raw.settings.theme = 2
|
|
||||||
if (parsed == 4) raw.settings.theme = 3
|
|
||||||
else if (parsed != 0 && parsed != 1) raw.settings.theme = 0
|
|
||||||
}
|
|
||||||
raw.version = version
|
|
||||||
writeNormalConfig(raw)
|
|
||||||
return NormalConfig.import(raw)
|
|
||||||
} catch {
|
|
||||||
return new NormalConfig(version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function writeNormalConfig (data: NormalConfig) {
|
|
||||||
const options = {
|
|
||||||
baseDir: BaseDirectory.AppLocalData
|
|
||||||
}
|
|
||||||
const doesFolderExist = await exists('', options)
|
|
||||||
const doesConfigExist = await exists('config.json', options)
|
|
||||||
if (!doesFolderExist || !doesConfigExist) {
|
|
||||||
if (!doesFolderExist) {
|
|
||||||
await mkdir('', options)
|
|
||||||
}
|
|
||||||
const file = await create('config.json', options)
|
|
||||||
await file.write(new TextEncoder().encode(JSON.stringify(data, null, 2)))
|
|
||||||
await file.close()
|
|
||||||
} else {
|
|
||||||
await writeFile(
|
|
||||||
'config.json',
|
|
||||||
new TextEncoder().encode(JSON.stringify(data, null, 2)),
|
|
||||||
options
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function readVersionsConfig (): Promise<VersionsConfig> {
|
export async function readVersionsConfig (): Promise<VersionsConfig> {
|
||||||
const version = await app.getVersion()
|
const version = await app.getVersion()
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { NormalConfig } from '@/types/NormalConfig'
|
import { writeText } from '@tauri-apps/plugin-clipboard-manager'
|
||||||
import { notifyUser } from './Notifications'
|
import { notifyUser } from './Notifications'
|
||||||
|
|
||||||
export async function copyToClipboard (
|
export async function copyToClipboard (
|
||||||
text: string,
|
text: string,
|
||||||
normalConfig: NormalConfig | null
|
notificationsAllowed: boolean
|
||||||
) {
|
) {
|
||||||
if (normalConfig?.settings.allowNotifications)
|
await writeText(text)
|
||||||
|
if (notificationsAllowed)
|
||||||
await notifyUser('Copied', 'Text "' + text + '" copied to clipboard')
|
await notifyUser('Copied', 'Text "' + text + '" copied to clipboard')
|
||||||
}
|
}
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { SettingsType } from './SettingsType'
|
|
||||||
|
|
||||||
type NormalConfigData = {
|
|
||||||
version: string
|
|
||||||
settings?: Partial<SettingsType>
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NormalConfig {
|
|
||||||
constructor (
|
|
||||||
public version: string,
|
|
||||||
public settings: SettingsType = new SettingsType()
|
|
||||||
) {}
|
|
||||||
|
|
||||||
static import (data: NormalConfigData) {
|
|
||||||
const cfg = new NormalConfig(data.version)
|
|
||||||
if (data.settings) {
|
|
||||||
cfg.settings = { ...cfg.settings, ...data.settings }
|
|
||||||
}
|
|
||||||
return cfg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
export class SettingsType {
|
|
||||||
constructor (
|
|
||||||
public allowNotifications: boolean = true,
|
|
||||||
public alwaysShowGamesInSidebar: boolean = true,
|
|
||||||
public useWineOnUnixWhenNeeded: boolean = false,
|
|
||||||
public wineOnUnixCommand: string = 'wine %path%',
|
|
||||||
public theme: number = 0
|
|
||||||
) {}
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user