You've already forked AstralRinth
forked from didirus/AstralRinth
Merge commit '7fa442fb28a2b9156690ff147206275163e7aec8' into beta
This commit is contained in:
@@ -1,55 +1,58 @@
|
||||
[package]
|
||||
name = "theseus_gui"
|
||||
version = "1.0.0-local" # The actual version is set by the theseus-build workflow on tagging
|
||||
description = "The Modrinth App is a desktop application for managing your Minecraft mods"
|
||||
license = "GPL-3.0-only"
|
||||
repository = "https://github.com/modrinth/code/apps/app/"
|
||||
# The actual version is set by the theseus-build workflow on tagging
|
||||
version = "1.0.0-local"
|
||||
edition.workspace = true
|
||||
description = "The Modrinth App is a desktop application for managing your Minecraft mods"
|
||||
repository = "https://github.com/modrinth/code/apps/app/"
|
||||
license = "GPL-3.0-only"
|
||||
|
||||
[dependencies]
|
||||
chrono = { workspace = true }
|
||||
daedalus = { workspace = true }
|
||||
dashmap = { workspace = true }
|
||||
either = { workspace = true }
|
||||
enumset = { workspace = true, features = ["serde"] }
|
||||
hyper = { workspace = true, features = ["server"] }
|
||||
hyper-util = { workspace = true }
|
||||
native-dialog = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
path-util = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
serde_with = { workspace = true }
|
||||
tauri = { workspace = true, features = [
|
||||
"devtools",
|
||||
"macos-private-api",
|
||||
"protocol-asset",
|
||||
] }
|
||||
tauri-plugin-deep-link = { workspace = true }
|
||||
tauri-plugin-dialog = { workspace = true }
|
||||
tauri-plugin-http = { workspace = true }
|
||||
tauri-plugin-opener = { workspace = true }
|
||||
tauri-plugin-os = { workspace = true }
|
||||
tauri-plugin-single-instance = { workspace = true }
|
||||
tauri-plugin-updater = { workspace = true }
|
||||
tauri-plugin-window-state = { workspace = true }
|
||||
theseus = { workspace = true, features = ["tauri"] }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["time"] }
|
||||
tracing = { workspace = true }
|
||||
tracing-error = { workspace = true }
|
||||
url = { workspace = true }
|
||||
urlencoding = { workspace = true }
|
||||
uuid = { workspace = true, features = ["serde", "v4"] }
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { workspace = true, features = ["codegen"] }
|
||||
|
||||
[dependencies]
|
||||
theseus = { workspace = true, features = ["tauri"] }
|
||||
|
||||
serde_json.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_with.workspace = true
|
||||
|
||||
tauri = { workspace = true, features = ["devtools", "macos-private-api", "protocol-asset"] }
|
||||
tauri-plugin-deep-link.workspace = true
|
||||
tauri-plugin-dialog.workspace = true
|
||||
tauri-plugin-http.workspace = true
|
||||
tauri-plugin-opener.workspace = true
|
||||
tauri-plugin-os.workspace = true
|
||||
tauri-plugin-single-instance.workspace = true
|
||||
tauri-plugin-updater.workspace = true
|
||||
tauri-plugin-window-state.workspace = true
|
||||
|
||||
tokio = { workspace = true, features = ["time"] }
|
||||
thiserror.workspace = true
|
||||
daedalus.workspace = true
|
||||
chrono.workspace = true
|
||||
either.workspace = true
|
||||
hyper = { workspace = true, features = ["server"] }
|
||||
hyper-util.workspace = true
|
||||
|
||||
url.workspace = true
|
||||
urlencoding.workspace = true
|
||||
uuid = { workspace = true, features = ["serde", "v4"] }
|
||||
|
||||
tracing.workspace = true
|
||||
tracing-error.workspace = true
|
||||
|
||||
dashmap.workspace = true
|
||||
paste.workspace = true
|
||||
enumset = { workspace = true, features = ["serde"] }
|
||||
|
||||
native-dialog.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
tauri-plugin-updater = { workspace = true, optional = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
webview2-com.workspace = true
|
||||
windows-core.workspace = true
|
||||
|
||||
[features]
|
||||
# by default Tauri runs in production mode
|
||||
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
||||
|
||||
@@ -231,6 +231,7 @@ fn main() {
|
||||
"apply_migration_fix",
|
||||
"init_update_launcher",
|
||||
"get_os",
|
||||
"is_network_metered",
|
||||
"should_disable_mouseover",
|
||||
"highlight_in_folder",
|
||||
"open_path",
|
||||
|
||||
19
apps/app/icons/apple.icon/Assets/Modrinth logo.svg
Normal file
19
apps/app/icons/apple.icon/Assets/Modrinth logo.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_6205_20699)">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M196.578 126.025C200.159 112.688 200.94 98.7557 198.873 85.1028C196.806 71.4499 191.934 58.3724 184.565 46.6928C177.196 35.0132 167.488 24.9843 156.053 17.2368C144.618 9.48941 131.703 4.19112 118.12 1.67522C104.537 -0.840683 90.5797 -0.519744 77.1267 2.61785C63.6738 5.75544 51.0159 11.6418 39.9494 19.9066C28.8828 28.1715 19.647 38.636 12.8229 50.642C5.99878 62.6479 1.73402 75.9356 0.296875 89.669H17.2335C19.5541 71.3251 27.9415 54.2826 41.0629 41.2496C54.1843 28.2167 71.2869 19.941 89.653 17.7376C108.019 15.5342 126.595 19.5296 142.429 29.0887C158.263 38.6479 170.447 53.2225 177.044 70.4965L160.588 74.8982C156.908 66.0554 151.338 58.1243 144.267 51.6612C137.196 45.198 128.797 40.3593 119.658 37.4843L116.623 54.597C125.592 57.8879 133.386 63.757 139.025 71.4657C144.664 79.1743 147.896 88.3781 148.314 97.9185C148.732 107.459 146.317 116.91 141.374 125.082C136.43 133.254 129.179 139.781 120.533 143.843L125.035 160.631C138.378 155.108 149.527 145.342 156.755 132.845C163.983 120.348 166.888 105.817 165.019 91.5031L181.42 87.1296C183.151 98.0656 182.643 109.239 179.924 119.973L196.578 126.025Z"
|
||||
fill="#1BD96A"
|
||||
/>
|
||||
<path
|
||||
d="M125.797 196.578C111.561 200.398 96.6581 201.029 82.1508 198.426C67.6435 195.823 53.8902 190.049 41.8733 181.519C29.8563 172.988 19.8721 161.91 12.6337 149.076C5.39538 136.242 1.08144 121.969 0 107.276H16.9366C17.3772 111.998 18.2176 116.676 19.4489 121.256C20.7334 126.061 22.4481 130.74 24.5722 135.237L39.6458 126.194C35.2357 115.982 33.4908 104.819 34.5746 93.7491C35.6584 82.6794 39.5355 72.0657 45.8425 62.9024C52.1496 53.739 60.6804 46.3258 70.6357 41.3571C80.5911 36.3883 91.6452 34.0267 102.763 34.4934L99.7001 51.6062C92.2843 51.64 84.975 53.3723 78.3335 56.6702C71.6919 59.968 65.895 64.7436 61.3879 70.6302C56.8808 76.5167 53.7835 83.3576 52.3339 90.6273C50.8843 97.8969 51.1209 105.402 53.0257 112.566C53.4632 114.16 53.929 115.74 54.5359 117.151L73.8153 105.597L68.0428 90.2475L86.2496 71.5546L109.269 66.6028L115.889 74.8277L105.275 85.5637L96.0305 88.4699L89.4111 95.2699L92.6573 104.271C92.6573 104.271 99.2061 111.24 99.2202 111.24L108.493 108.785L115.084 101.548L129.48 96.9769L133.714 106.627L118.923 124.812L94.0264 132.67L82.8482 120.241L63.3852 131.936C68.3953 137.636 74.6712 142.083 81.7102 144.923C88.7492 147.762 96.3556 148.914 103.92 148.287L108.422 165.118C97.1879 166.584 85.7649 165.124 75.2606 160.88C64.7563 156.636 55.5269 149.752 48.4669 140.895L33.4498 149.895C42.0264 161.15 53.3399 170.026 66.315 175.679C79.2902 181.331 93.496 183.574 107.582 182.193C121.669 180.811 135.168 175.853 146.797 167.787C158.426 159.722 167.798 148.818 174.024 136.112L190.678 142.164C184.512 155.432 175.504 167.182 164.293 176.585C153.081 185.988 139.939 192.813 125.797 196.578Z"
|
||||
fill="#1BD96A"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_6205_20699">
|
||||
<rect width="200" height="200" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
73
apps/app/icons/apple.icon/icon.json
Normal file
73
apps/app/icons/apple.icon/icon.json
Normal file
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"fill": {
|
||||
"linear-gradient": [
|
||||
"display-p3:0.18127,0.41301,0.28212,1.00000",
|
||||
"display-p3:0.09652,0.20392,0.15411,1.00000"
|
||||
]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"blur-material": 0.5,
|
||||
"layers": [
|
||||
{
|
||||
"blend-mode-specializations": [
|
||||
{
|
||||
"appearance": "tinted",
|
||||
"value": "normal"
|
||||
}
|
||||
],
|
||||
"fill-specializations": [
|
||||
{
|
||||
"value": {
|
||||
"linear-gradient": [
|
||||
"display-p3:0.49491,0.92226,0.57293,1.00000",
|
||||
"display-p3:0.10588,0.85098,0.41569,1.00000"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"appearance": "tinted",
|
||||
"value": "automatic"
|
||||
}
|
||||
],
|
||||
"glass": true,
|
||||
"image-name": "Modrinth logo.svg",
|
||||
"name": "Modrinth logo",
|
||||
"opacity-specializations": [
|
||||
{
|
||||
"appearance": "tinted",
|
||||
"value": 1
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"scale": 4.1,
|
||||
"translation-in-points": [0, 0]
|
||||
}
|
||||
}
|
||||
],
|
||||
"lighting": "individual",
|
||||
"opacity-specializations": [
|
||||
{
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"appearance": "tinted",
|
||||
"value": 1
|
||||
}
|
||||
],
|
||||
"shadow": {
|
||||
"kind": "neutral",
|
||||
"opacity": 0.5
|
||||
},
|
||||
"specular": true,
|
||||
"translucency": {
|
||||
"enabled": true,
|
||||
"value": 0.5
|
||||
}
|
||||
}
|
||||
],
|
||||
"supported-platforms": {
|
||||
"circles": ["watchOS"],
|
||||
"squares": "shared"
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,9 @@
|
||||
"build": "tauri build",
|
||||
"dev": "tauri dev",
|
||||
"test": "cargo nextest run --all-targets --no-fail-fast",
|
||||
"lint": "cargo fmt --check && cargo clippy --all-targets",
|
||||
"lint": "cargo fmt --check && cargo clippy --all-targets && cargo clippy --all-targets --features updater",
|
||||
"lint:ancillary": "prettier --check .",
|
||||
"fix": "cargo clippy --all-targets --fix --allow-dirty && cargo fmt",
|
||||
"fix": "cargo clippy --all-targets --fix --allow-dirty && cargo clippy --all-targets --features updater --fix --allow-dirty && cargo fmt",
|
||||
"fix:ancillary": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -46,8 +46,12 @@ pub enum TheseusSerializableError {
|
||||
Tauri(#[from] tauri::Error),
|
||||
|
||||
#[cfg(feature = "updater")]
|
||||
#[error("Tauri updater error: {0}")]
|
||||
TauriUpdater(#[from] tauri_plugin_updater::Error),
|
||||
#[error("Updater error: {0}")]
|
||||
Updater(#[from] tauri_plugin_updater::Error),
|
||||
|
||||
#[cfg(feature = "updater")]
|
||||
#[error("HTTP error: {0}")]
|
||||
Http(#[from] tauri_plugin_http::reqwest::Error),
|
||||
}
|
||||
|
||||
// Generic implementation of From<T> for ErrorTypeA
|
||||
@@ -105,5 +109,6 @@ impl_serialize! {
|
||||
impl_serialize! {
|
||||
IO,
|
||||
Tauri,
|
||||
TauriUpdater,
|
||||
Updater,
|
||||
Http,
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
//! [RFC 8252]: https://datatracker.ietf.org/doc/html/rfc8252
|
||||
|
||||
use std::{
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||
net::SocketAddr,
|
||||
sync::{LazyLock, Mutex},
|
||||
time::Duration,
|
||||
};
|
||||
@@ -19,10 +19,8 @@ use std::{
|
||||
use hyper::body::Incoming;
|
||||
use hyper_util::rt::{TokioIo, TokioTimer};
|
||||
use theseus::ErrorKind;
|
||||
use tokio::{
|
||||
net::TcpListener,
|
||||
sync::{broadcast, oneshot},
|
||||
};
|
||||
use theseus::prelude::tcp_listen_any_loopback;
|
||||
use tokio::sync::{broadcast, oneshot};
|
||||
|
||||
static SERVER_SHUTDOWN: LazyLock<broadcast::Sender<()>> =
|
||||
LazyLock::new(|| broadcast::channel(1024).0);
|
||||
@@ -35,17 +33,7 @@ static SERVER_SHUTDOWN: LazyLock<broadcast::Sender<()>> =
|
||||
pub async fn listen(
|
||||
listen_socket_tx: oneshot::Sender<Result<SocketAddr, theseus::Error>>,
|
||||
) -> Result<Option<String>, theseus::Error> {
|
||||
// IPv4 is tried first for the best compatibility and performance with most systems.
|
||||
// IPv6 is also tried in case IPv4 is not available. Resolving "localhost" is avoided
|
||||
// to prevent failures deriving from improper name resolution setup. Any available
|
||||
// ephemeral port is used to prevent conflicts with other services. This is all as per
|
||||
// RFC 8252's recommendations
|
||||
const ANY_LOOPBACK_SOCKET: &[SocketAddr] = &[
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
|
||||
SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 0),
|
||||
];
|
||||
|
||||
let listener = match TcpListener::bind(ANY_LOOPBACK_SOCKET).await {
|
||||
let listener = match tcp_listen_any_loopback().await {
|
||||
Ok(listener) => {
|
||||
listen_socket_tx
|
||||
.send(listener.local_addr().map_err(|e| {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,6 @@
|
||||
use crate::api::Result;
|
||||
use dashmap::DashMap;
|
||||
use path_util::SafeRelativeUtf8UnixPathBuf;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -239,7 +240,7 @@ pub async fn profile_export_mrpack(
|
||||
#[tauri::command]
|
||||
pub async fn profile_get_pack_export_candidates(
|
||||
profile_path: &str,
|
||||
) -> Result<Vec<String>> {
|
||||
) -> Result<Vec<SafeRelativeUtf8UnixPathBuf>> {
|
||||
let candidates = profile::get_pack_export_candidates(profile_path).await?;
|
||||
Ok(candidates)
|
||||
}
|
||||
@@ -271,7 +272,7 @@ pub struct EditProfile {
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
with = "::serde_with::rust::double_option"
|
||||
with = "serde_with::rust::double_option"
|
||||
)]
|
||||
pub loader_version: Option<Option<String>>,
|
||||
|
||||
@@ -280,45 +281,45 @@ pub struct EditProfile {
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
with = "::serde_with::rust::double_option"
|
||||
with = "serde_with::rust::double_option"
|
||||
)]
|
||||
pub linked_data: Option<Option<LinkedData>>,
|
||||
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
with = "::serde_with::rust::double_option"
|
||||
with = "serde_with::rust::double_option"
|
||||
)]
|
||||
pub java_path: Option<Option<String>>,
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
with = "::serde_with::rust::double_option"
|
||||
with = "serde_with::rust::double_option"
|
||||
)]
|
||||
pub extra_launch_args: Option<Option<Vec<String>>>,
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
with = "::serde_with::rust::double_option"
|
||||
with = "serde_with::rust::double_option"
|
||||
)]
|
||||
pub custom_env_vars: Option<Option<Vec<(String, String)>>>,
|
||||
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
with = "::serde_with::rust::double_option"
|
||||
with = "serde_with::rust::double_option"
|
||||
)]
|
||||
pub memory: Option<Option<MemorySettings>>,
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
with = "::serde_with::rust::double_option"
|
||||
with = "serde_with::rust::double_option"
|
||||
)]
|
||||
pub force_fullscreen: Option<Option<bool>>,
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
with = "::serde_with::rust::double_option"
|
||||
with = "serde_with::rust::double_option"
|
||||
)]
|
||||
pub game_resolution: Option<Option<WindowSize>>,
|
||||
pub hooks: Option<Hooks>,
|
||||
|
||||
@@ -13,13 +13,14 @@ use theseus::prelude::canonicalize;
|
||||
use theseus::util::utils;
|
||||
use url::Url;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
pub fn init<R: Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("utils")
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
init_authlib_patching,
|
||||
apply_migration_fix,
|
||||
init_update_launcher,
|
||||
get_os,
|
||||
is_network_metered,
|
||||
should_disable_mouseover,
|
||||
highlight_in_folder,
|
||||
open_path,
|
||||
@@ -66,6 +67,14 @@ pub async fn init_update_launcher(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub enum OS {
|
||||
Windows,
|
||||
Linux,
|
||||
MacOS,
|
||||
}
|
||||
|
||||
/// Gets OS
|
||||
#[tauri::command]
|
||||
pub fn get_os() -> OS {
|
||||
@@ -78,12 +87,9 @@ pub fn get_os() -> OS {
|
||||
os
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub enum OS {
|
||||
Windows,
|
||||
Linux,
|
||||
MacOS,
|
||||
#[tauri::command]
|
||||
pub async fn is_network_metered() -> Result<bool> {
|
||||
Ok(theseus::prelude::is_network_metered().await?)
|
||||
}
|
||||
|
||||
// Lists active progress bars
|
||||
|
||||
@@ -14,77 +14,18 @@ mod error;
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
|
||||
#[cfg(feature = "updater")]
|
||||
mod updater_impl;
|
||||
#[cfg(not(feature = "updater"))]
|
||||
mod updater_impl_noop;
|
||||
|
||||
// Should be called in launcher initialization
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[tauri::command]
|
||||
async fn initialize_state(app: tauri::AppHandle) -> api::Result<()> {
|
||||
tracing::info!("Initializing app event state...");
|
||||
theseus::EventState::init(app.clone()).await?;
|
||||
|
||||
// #[cfg(feature = "updater")]
|
||||
// 'updater: {
|
||||
// if env::var("MODRINTH_EXTERNAL_UPDATE_PROVIDER").is_ok() {
|
||||
// State::init().await?;
|
||||
// break 'updater;
|
||||
// }
|
||||
|
||||
// use tauri_plugin_updater::UpdaterExt;
|
||||
|
||||
// let updater = app.updater_builder().build()?;
|
||||
|
||||
// let update_fut = updater.check();
|
||||
|
||||
// let check_bar = theseus::init_loading(
|
||||
// theseus::LoadingBarType::CheckingForUpdates,
|
||||
// 1.0,
|
||||
// "Checking for updates...",
|
||||
// )
|
||||
// .await?;
|
||||
|
||||
// tracing::info!("Checking for updates...");
|
||||
// let update = update_fut.await;
|
||||
|
||||
// drop(check_bar);
|
||||
|
||||
// if let Some(update) = update.ok().flatten() {
|
||||
// tracing::info!("Update found: {:?}", update.download_url);
|
||||
// let loader_bar_id = theseus::init_loading(
|
||||
// theseus::LoadingBarType::LauncherUpdate {
|
||||
// version: update.version.clone(),
|
||||
// current_version: update.current_version.clone(),
|
||||
// },
|
||||
// 1.0,
|
||||
// "Updating Modrinth App...",
|
||||
// )
|
||||
// .await?;
|
||||
|
||||
// // 100 MiB
|
||||
// const DEFAULT_CONTENT_LENGTH: u64 = 1024 * 1024 * 100;
|
||||
|
||||
// update
|
||||
// .download_and_install(
|
||||
// |chunk_length, content_length| {
|
||||
// let _ = theseus::emit_loading(
|
||||
// &loader_bar_id,
|
||||
// (chunk_length as f64)
|
||||
// / (content_length
|
||||
// .unwrap_or(DEFAULT_CONTENT_LENGTH)
|
||||
// as f64),
|
||||
// None,
|
||||
// );
|
||||
// },
|
||||
// || {},
|
||||
// )
|
||||
// .await?;
|
||||
|
||||
// app.restart();
|
||||
// }
|
||||
// }
|
||||
|
||||
// #[cfg(not(feature = "updater"))]
|
||||
// {
|
||||
// }
|
||||
tracing::info!("Initializing app state...");
|
||||
tracing::info!("Initializing app state...");
|
||||
State::init().await?;
|
||||
tracing::info!("AstralRinth state successfully initialized.");
|
||||
let state = State::get().await?;
|
||||
@@ -122,6 +63,18 @@ fn is_dev() -> bool {
|
||||
cfg!(debug_assertions)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn are_updates_enabled() -> bool {
|
||||
cfg!(feature = "updater")
|
||||
&& env::var("MODRINTH_EXTERNAL_UPDATE_PROVIDER").is_err()
|
||||
}
|
||||
|
||||
#[cfg(feature = "updater")]
|
||||
pub use updater_impl::*;
|
||||
|
||||
#[cfg(not(feature = "updater"))]
|
||||
pub use updater_impl_noop::*;
|
||||
|
||||
// Toggles decorations
|
||||
#[tauri::command]
|
||||
async fn toggle_decorations(b: bool, window: tauri::Window) -> api::Result<()> {
|
||||
@@ -161,11 +114,6 @@ fn main() {
|
||||
|
||||
let mut builder = tauri::Builder::default();
|
||||
|
||||
// #[cfg(feature = "updater")]
|
||||
// {
|
||||
// builder = builder.plugin(tauri_plugin_updater::Builder::new().build());
|
||||
// }
|
||||
|
||||
builder = builder
|
||||
.plugin(tauri_plugin_single_instance::init(|app, args, _cwd| {
|
||||
if let Some(payload) = args.get(1) {
|
||||
@@ -270,9 +218,14 @@ fn main() {
|
||||
.plugin(api::cache::init())
|
||||
.plugin(api::friends::init())
|
||||
.plugin(api::worlds::init())
|
||||
.manage(PendingUpdateData::default())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
initialize_state,
|
||||
is_dev,
|
||||
are_updates_enabled,
|
||||
get_update_size,
|
||||
enqueue_update_for_installation,
|
||||
remove_enqueued_update,
|
||||
toggle_decorations,
|
||||
show_window,
|
||||
restart_app,
|
||||
@@ -284,8 +237,42 @@ fn main() {
|
||||
match app {
|
||||
Ok(app) => {
|
||||
app.run(|app, event| {
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[cfg(not(any(feature = "updater", target_os = "macos")))]
|
||||
drop((app, event));
|
||||
|
||||
#[cfg(feature = "updater")]
|
||||
if matches!(event, tauri::RunEvent::Exit) {
|
||||
let update_data = app.state::<PendingUpdateData>().inner();
|
||||
if let Some((update, data)) = &*update_data.0.lock().unwrap() {
|
||||
fn set_changelog_toast(version: Option<String>) {
|
||||
let toast_result: theseus::Result<()> = tauri::async_runtime::block_on(async move {
|
||||
let mut settings = settings::get().await?;
|
||||
settings.pending_update_toast_for_version = version;
|
||||
settings::set(settings).await?;
|
||||
Ok(())
|
||||
});
|
||||
if let Err(e) = toast_result {
|
||||
tracing::warn!("Failed to set pending_update_toast: {e}")
|
||||
}
|
||||
}
|
||||
|
||||
set_changelog_toast(Some(update.version.clone()));
|
||||
if let Err(e) = update.install(data) {
|
||||
tracing::error!("Error while updating: {e}");
|
||||
set_changelog_toast(None);
|
||||
|
||||
DialogBuilder::message()
|
||||
.set_level(MessageLevel::Error)
|
||||
.set_title("Update error")
|
||||
.set_text(format!("Failed to install update due to an error:\n{e}"))
|
||||
.alert()
|
||||
.show()
|
||||
.unwrap();
|
||||
}
|
||||
app.restart();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
if let tauri::RunEvent::Opened { urls } = event {
|
||||
tracing::info!("Handling webview open {urls:?}");
|
||||
@@ -313,6 +300,8 @@ fn main() {
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Error while running tauri application: {:?}", e);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// tauri doesn't expose runtime errors, so matching a string representation seems like the only solution
|
||||
@@ -341,7 +330,6 @@ fn main() {
|
||||
.show()
|
||||
.unwrap();
|
||||
|
||||
tracing::error!("Error while running tauri application: {:?}", e);
|
||||
panic!("{1}: {:?}", e, "error while running tauri application")
|
||||
}
|
||||
}
|
||||
|
||||
121
apps/app/src/updater_impl.rs
Normal file
121
apps/app/src/updater_impl.rs
Normal file
@@ -0,0 +1,121 @@
|
||||
use crate::api::Result;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tauri::http::HeaderValue;
|
||||
use tauri::http::header::ACCEPT;
|
||||
use tauri::{Manager, ResourceId, Runtime, Webview};
|
||||
use tauri_plugin_http::reqwest;
|
||||
use tauri_plugin_http::reqwest::ClientBuilder;
|
||||
use tauri_plugin_updater::Error;
|
||||
use tauri_plugin_updater::Update;
|
||||
use theseus::{
|
||||
LAUNCHER_USER_AGENT, LoadingBarType, emit_loading, init_loading,
|
||||
};
|
||||
use tokio::time::Instant;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PendingUpdateData(pub Mutex<Option<(Arc<Update>, Vec<u8>)>>);
|
||||
|
||||
// Reimplementation of Update::download mostly, minus the actual download part
|
||||
#[tauri::command]
|
||||
pub async fn get_update_size<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
rid: ResourceId,
|
||||
) -> Result<Option<u64>> {
|
||||
let update = webview.resources_table().get::<Update>(rid)?;
|
||||
|
||||
let mut headers = update.headers.clone();
|
||||
if !headers.contains_key(ACCEPT) {
|
||||
headers.insert(
|
||||
ACCEPT,
|
||||
HeaderValue::from_static("application/octet-stream"),
|
||||
);
|
||||
}
|
||||
|
||||
let mut request = ClientBuilder::new().user_agent(LAUNCHER_USER_AGENT);
|
||||
if let Some(timeout) = update.timeout {
|
||||
request = request.timeout(timeout);
|
||||
}
|
||||
if let Some(ref proxy) = update.proxy {
|
||||
let proxy = reqwest::Proxy::all(proxy.as_str())?;
|
||||
request = request.proxy(proxy);
|
||||
}
|
||||
let response = request
|
||||
.build()?
|
||||
.head(update.download_url.clone())
|
||||
.headers(headers)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(Error::Network(format!(
|
||||
"Download request failed with status: {}",
|
||||
response.status()
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
let content_length = response
|
||||
.headers()
|
||||
.get("Content-Length")
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.and_then(|value| value.parse().ok());
|
||||
|
||||
Ok(content_length)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn enqueue_update_for_installation<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
rid: ResourceId,
|
||||
) -> Result<()> {
|
||||
let pending_data = webview.state::<PendingUpdateData>().inner();
|
||||
|
||||
let update = webview.resources_table().get::<Update>(rid)?;
|
||||
|
||||
let progress = init_loading(
|
||||
LoadingBarType::LauncherUpdate {
|
||||
version: update.version.clone(),
|
||||
current_version: update.current_version.clone(),
|
||||
},
|
||||
1.0,
|
||||
"Downloading update...",
|
||||
)
|
||||
.await?;
|
||||
|
||||
let download_start = Instant::now();
|
||||
let update_data = update
|
||||
.download(
|
||||
|chunk_size, total_size| {
|
||||
let Some(total_size) = total_size else {
|
||||
return;
|
||||
};
|
||||
if let Err(e) = emit_loading(
|
||||
&progress,
|
||||
chunk_size as f64 / total_size as f64,
|
||||
None,
|
||||
) {
|
||||
tracing::error!(
|
||||
"Failed to update download progress bar: {e}"
|
||||
);
|
||||
}
|
||||
},
|
||||
|| {},
|
||||
)
|
||||
.await?;
|
||||
let download_duration = download_start.elapsed();
|
||||
tracing::info!("Downloaded update in {download_duration:?}");
|
||||
|
||||
pending_data
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.replace((update, update_data));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn remove_enqueued_update<R: Runtime>(webview: Webview<R>) {
|
||||
let pending_data = webview.state::<PendingUpdateData>().inner();
|
||||
pending_data.0.lock().unwrap().take();
|
||||
}
|
||||
26
apps/app/src/updater_impl_noop.rs
Normal file
26
apps/app/src/updater_impl_noop.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use crate::api::Result;
|
||||
use theseus::ErrorKind;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PendingUpdateData(());
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_update_size() -> Result<()> {
|
||||
updates_are_disabled()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn enqueue_update_for_installation() -> Result<()> {
|
||||
updates_are_disabled()
|
||||
}
|
||||
|
||||
fn updates_are_disabled() -> Result<()> {
|
||||
let error: theseus::Error = ErrorKind::OtherError(
|
||||
"Updates are disabled in this build.".to_string(),
|
||||
)
|
||||
.into();
|
||||
Err(error.into())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn remove_enqueued_update() {}
|
||||
@@ -48,7 +48,7 @@
|
||||
]
|
||||
},
|
||||
"productName": "AstralRinth App",
|
||||
"version": "0.10.601",
|
||||
"version": "0.10.1101",
|
||||
"mainBinaryName": "AstralRinth App",
|
||||
"identifier": "AstralRinthApp",
|
||||
"plugins": {
|
||||
@@ -76,7 +76,7 @@
|
||||
"width": 1280,
|
||||
"minHeight": 700,
|
||||
"minWidth": 1100,
|
||||
"visible": true,
|
||||
"visible": false,
|
||||
"zoomHotkeysEnabled": false,
|
||||
"decorations": false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user