You've already forked AstralRinth
forked from didirus/AstralRinth
Reworked app update flow (#3960)
* Make theseus capable of logging messages from the `log` crate * Move update checking entirely into JS and open a modal if an update is available * Fix formatjs on Windows and run formatjs * Add in the buttons and body * Fix lint * Show update size in modal * Fix update not being rechecked if the update modal was directly dismissed * Slight UI tweaks * Fix lint * Implement skipping the update * Implement the Update Now button * Implement updating at next exit * Turn download progress into an error bar on failure * Restore 5 minute update check instead of 30 seconds * Fix PendingUpdateData being seen as a unit struct * Fix lint * Make CI also lint updater code * feat: create AppearingProgressBar component * feat: polish update available modal * feat: add error handling * Open changelog with tauri-plugin-opener * Run intl:extract * Update completion toasts (#3978) * Use single LAUNCHER_USER_AGENT constant for all user agents * Fix build on Mac * Request the update size with HEAD instead of GET * UI tweaks * lint * Fix lint * fix: hide modal header & add "Hide update reminder" button w/ tooltip * Run intl:extract * fix: lint issues * fix: merge issues * notifications.js no longer exists * Add metered network checking * Add a timeout to macOS is_network_metered * Fix tauri.conf.json * vibe debugging * Set a dispatch queue * Have a popup that asks you if you'd like to disable automatic file downloads if you're on a metered network * Move UpdateModal to modal package * Fix lint * Add a toggle for automatic downloads * Fix type Co-authored-by: Alejandro González <7822554+AlexTMjugador@users.noreply.github.com> Signed-off-by: Josiah Glosson <soujournme@gmail.com> * Redo updating UI and experience * lint * fix unlistener issue * remove unneeded translation keys * Fix expose issue * temp disable cranelift, tweak some messages * change version back * Clean up App.vue * move toast to top right * update reload icon * Fixed the bug!!!!!!!!!!!! * improve messages * intl:extract * Add liquid glass icon file * not you! * use dependency injection * lint on apple icon * Fix imports, move download size to button * change update check back to 5 mins * lint + move to providers * intl:extract --------- Signed-off-by: Cal H. <hendersoncal117@gmail.com> Signed-off-by: Josiah Glosson <soujournme@gmail.com> Co-authored-by: Calum <calum@modrinth.com> Co-authored-by: Prospector <prospectordev@gmail.com> Co-authored-by: Cal H. <hendersoncal117@gmail.com> Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com> Co-authored-by: Alejandro González <7822554+AlexTMjugador@users.noreply.github.com>
This commit is contained in:
@@ -37,7 +37,7 @@ pub mod prelude {
|
||||
settings,
|
||||
util::{
|
||||
io::{IOError, canonicalize},
|
||||
network::tcp_listen_any_loopback,
|
||||
network::{is_network_metered, tcp_listen_any_loopback},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -166,6 +166,13 @@ pub enum ErrorKind {
|
||||
|
||||
#[error("RPC error: {0}")]
|
||||
RpcError(String),
|
||||
|
||||
#[cfg(windows)]
|
||||
#[error("Windows error: {0}")]
|
||||
WindowsError(#[from] windows_core::Error),
|
||||
|
||||
#[error("zbus error: {0}")]
|
||||
ZbusError(#[from] zbus::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -176,7 +176,6 @@ pub enum LoadingBarType {
|
||||
import_location: PathBuf,
|
||||
profile_name: String,
|
||||
},
|
||||
CheckingForUpdates,
|
||||
LauncherUpdate {
|
||||
version: String,
|
||||
current_version: String,
|
||||
|
||||
@@ -25,3 +25,9 @@ pub use event::{
|
||||
};
|
||||
pub use logger::start_logger;
|
||||
pub use state::State;
|
||||
|
||||
pub const LAUNCHER_USER_AGENT: &str = concat!(
|
||||
"modrinth/theseus/",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
" (support@modrinth.com)"
|
||||
);
|
||||
|
||||
@@ -25,12 +25,11 @@ pub fn start_logger() -> Option<()> {
|
||||
.unwrap_or_else(|_| {
|
||||
tracing_subscriber::EnvFilter::new("theseus=info,theseus_gui=info")
|
||||
});
|
||||
let subscriber = tracing_subscriber::registry()
|
||||
tracing_subscriber::registry()
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.with(filter)
|
||||
.with(tracing_error::ErrorLayer::default());
|
||||
tracing::subscriber::set_global_default(subscriber)
|
||||
.expect("setting default subscriber failed");
|
||||
.with(tracing_error::ErrorLayer::default())
|
||||
.init();
|
||||
Some(())
|
||||
}
|
||||
|
||||
@@ -76,7 +75,7 @@ pub fn start_logger() -> Option<()> {
|
||||
let filter = tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("theseus=info"));
|
||||
|
||||
let subscriber = tracing_subscriber::registry()
|
||||
tracing_subscriber::registry()
|
||||
.with(
|
||||
tracing_subscriber::fmt::layer()
|
||||
.with_writer(file)
|
||||
@@ -84,10 +83,8 @@ pub fn start_logger() -> Option<()> {
|
||||
.with_timer(ChronoLocal::rfc_3339()),
|
||||
)
|
||||
.with(filter)
|
||||
.with(tracing_error::ErrorLayer::default());
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber)
|
||||
.expect("Setting default subscriber failed");
|
||||
.with(tracing_error::ErrorLayer::default())
|
||||
.init();
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::ErrorKind;
|
||||
use crate::LAUNCHER_USER_AGENT;
|
||||
use crate::data::ModrinthCredentials;
|
||||
use crate::event::FriendPayload;
|
||||
use crate::event::emit::emit_friend;
|
||||
@@ -82,13 +83,9 @@ impl FriendsSocket {
|
||||
)
|
||||
.into_client_request()?;
|
||||
|
||||
let user_agent = format!(
|
||||
"modrinth/theseus/{} (support@modrinth.com)",
|
||||
env!("CARGO_PKG_VERSION")
|
||||
);
|
||||
request.headers_mut().insert(
|
||||
"User-Agent",
|
||||
HeaderValue::from_str(&user_agent).unwrap(),
|
||||
HeaderValue::from_str(LAUNCHER_USER_AGENT).unwrap(),
|
||||
);
|
||||
|
||||
let res = connect_async(request).await;
|
||||
|
||||
@@ -38,6 +38,10 @@ pub struct Settings {
|
||||
|
||||
pub developer_mode: bool,
|
||||
pub feature_flags: HashMap<FeatureFlag, bool>,
|
||||
|
||||
pub skipped_update: Option<String>,
|
||||
pub pending_update_toast_for_version: Option<String>,
|
||||
pub auto_download_updates: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, Hash, PartialEq)]
|
||||
@@ -63,7 +67,8 @@ impl Settings {
|
||||
json(extra_launch_args) extra_launch_args, json(custom_env_vars) custom_env_vars,
|
||||
mc_memory_max, mc_force_fullscreen, mc_game_resolution_x, mc_game_resolution_y, hide_on_process_start,
|
||||
hook_pre_launch, hook_wrapper, hook_post_exit,
|
||||
custom_dir, prev_custom_dir, migrated, json(feature_flags) feature_flags, toggle_sidebar
|
||||
custom_dir, prev_custom_dir, migrated, json(feature_flags) feature_flags, toggle_sidebar,
|
||||
skipped_update, pending_update_toast_for_version, auto_download_updates
|
||||
FROM settings
|
||||
"
|
||||
)
|
||||
@@ -117,6 +122,10 @@ impl Settings {
|
||||
.as_ref()
|
||||
.and_then(|x| serde_json::from_str(x).ok())
|
||||
.unwrap_or_default(),
|
||||
skipped_update: res.skipped_update,
|
||||
pending_update_toast_for_version: res
|
||||
.pending_update_toast_for_version,
|
||||
auto_download_updates: res.auto_download_updates.map(|x| x == 1),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -170,7 +179,11 @@ impl Settings {
|
||||
|
||||
toggle_sidebar = $26,
|
||||
feature_flags = $27,
|
||||
hide_nametag_skins_page = $28
|
||||
hide_nametag_skins_page = $28,
|
||||
|
||||
skipped_update = $29,
|
||||
pending_update_toast_for_version = $30,
|
||||
auto_download_updates = $31
|
||||
",
|
||||
max_concurrent_writes,
|
||||
max_concurrent_downloads,
|
||||
@@ -199,7 +212,10 @@ impl Settings {
|
||||
self.migrated,
|
||||
self.toggle_sidebar,
|
||||
feature_flags,
|
||||
self.hide_nametag_skins_page
|
||||
self.hide_nametag_skins_page,
|
||||
self.skipped_update,
|
||||
self.pending_update_toast_for_version,
|
||||
self.auto_download_updates,
|
||||
)
|
||||
.execute(exec)
|
||||
.await?;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Functions for fetching information from the Internet
|
||||
use super::io::{self, IOError};
|
||||
use crate::ErrorKind;
|
||||
use crate::LAUNCHER_USER_AGENT;
|
||||
use crate::event::LoadingBarId;
|
||||
use crate::event::emit::emit_loading;
|
||||
use bytes::Bytes;
|
||||
@@ -20,11 +21,8 @@ pub struct FetchSemaphore(pub Semaphore);
|
||||
|
||||
pub static REQWEST_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
|
||||
let mut headers = reqwest::header::HeaderMap::new();
|
||||
let header = reqwest::header::HeaderValue::from_str(&format!(
|
||||
"modrinth/theseus/{} (support@modrinth.com)",
|
||||
env!("CARGO_PKG_VERSION")
|
||||
))
|
||||
.unwrap();
|
||||
let header =
|
||||
reqwest::header::HeaderValue::from_str(LAUNCHER_USER_AGENT).unwrap();
|
||||
headers.insert(reqwest::header::USER_AGENT, header);
|
||||
reqwest::Client::builder()
|
||||
.tcp_keepalive(Some(time::Duration::from_secs(10)))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::Result;
|
||||
use std::io;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use tokio::net::TcpListener;
|
||||
@@ -15,3 +16,78 @@ pub async fn tcp_listen_any_loopback() -> io::Result<TcpListener> {
|
||||
|
||||
TcpListener::bind(ANY_LOOPBACK_SOCKET).await
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub async fn is_network_metered() -> Result<bool> {
|
||||
use windows::Networking::Connectivity::{
|
||||
NetworkCostType, NetworkInformation,
|
||||
};
|
||||
|
||||
let cost_type = NetworkInformation::GetInternetConnectionProfile()?
|
||||
.GetConnectionCost()?
|
||||
.NetworkCostType()?;
|
||||
Ok(matches!(
|
||||
cost_type,
|
||||
NetworkCostType::Fixed | NetworkCostType::Variable
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub async fn is_network_metered() -> Result<bool> {
|
||||
use crate::ErrorKind;
|
||||
use cidre::dispatch::Queue;
|
||||
use cidre::nw::PathMonitor;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio_util::future::FutureExt;
|
||||
|
||||
let (sender, mut receiver) = mpsc::channel(1);
|
||||
|
||||
let queue = Queue::new();
|
||||
let mut monitor = PathMonitor::new();
|
||||
monitor.set_queue(&queue);
|
||||
monitor.set_update_handler(move |path| {
|
||||
let _ = sender.try_send(path.is_constrained() || path.is_expensive());
|
||||
});
|
||||
|
||||
monitor.start();
|
||||
let result = receiver
|
||||
.recv()
|
||||
.timeout(Duration::from_millis(100))
|
||||
.await
|
||||
.ok()
|
||||
.flatten();
|
||||
monitor.cancel();
|
||||
|
||||
result.ok_or_else(|| {
|
||||
ErrorKind::OtherError(
|
||||
"NWPathMonitor didn't provide an NWPath in time".to_string(),
|
||||
)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub async fn is_network_metered() -> Result<bool> {
|
||||
// Thanks to https://github.com/Hakanbaban53/rclone-manager for showing how to do this
|
||||
use zbus::{Connection, Proxy};
|
||||
|
||||
let connection = Connection::system().await?;
|
||||
let proxy = Proxy::new(
|
||||
&connection,
|
||||
"org.freedesktop.NetworkManager",
|
||||
"/org/freedesktop/NetworkManager",
|
||||
"org.freedesktop.NetworkManager",
|
||||
)
|
||||
.await?;
|
||||
let metered = proxy.get_property("Metered").await?;
|
||||
Ok(matches!(metered, 1 | 3))
|
||||
}
|
||||
|
||||
#[cfg(not(any(windows, target_os = "macos", target_os = "linux")))]
|
||||
pub async fn is_network_metered() -> Result<bool> {
|
||||
tracing::warn!(
|
||||
"is_network_metered called on unsupported platform. Assuming unmetered."
|
||||
);
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user