Files
Rocketmc/packages/app-lib/src/util/network.rs
Josiah Glosson a538b99c18 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>
2025-09-29 15:28:31 +00:00

94 lines
2.8 KiB
Rust

use crate::Result;
use std::io;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use tokio::net::TcpListener;
pub async fn tcp_listen_any_loopback() -> io::Result<TcpListener> {
// 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),
];
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)
}