1
0
Files
AstralRinth/apps/app/src/main.rs
Josiah Glosson de3019e92b Theseus build updates (#3588)
* Add launcher_feature_version to Profile

* Misc fixes

- Add typing to theme and settings stuff
- Push instance route on creation from installing a modpack
- Fixed servers not reloading properly when first added

* Make old instances scan the logs folder for joined servers on launcher startup

* Create AttachedWorldData

* Change AttachedWorldData interface

* Rename WorldType::World to WorldType::Singleplayer

* Implement world display status system

* Fix Minecraft font

* Fix set_world_display_status Tauri error

* Add 'Play instance' option

* Add option to disable worlds showing in Home

* Fixes

- Fix available server filter only showing if there are some available
- Fixed server and singleplayer filters sometimes showing when there are only servers or singleplayer worlds
- Fixed new worlds not being automatically added when detected
- Rephrased Jump back into worlds option description

* Fixed sometimes more than 6 items showing up in Jump back in

* Fix servers.dat issue with instances you haven't played before

* Update a bunch of app dependencies in non-breaking ways

* Update dependencies in app-lib that had breaking updates

* Update dependencies in app that had breaking updates

* Fix too large of bulk requests being made, limit max to 800 #3430

* Also update tauri-plugin-opener

* Update app-lib to Rust 2024

* Non-breaking updates in ariadne

* Breaking updates in ariadne

* Ariadne Rust 2024

* Add hiding from home page, add types to Mods.vue

* Make recent worlds go into grid when display is huge

* Fix lint

* Remove redundant media query

* Fix protocol version on home page, and home page being blocked by pinging servers

* Clippy fix in app-lib

* Clippy fix in app

* Clippy fix

* More Clippy fixes

* Fix Prettier lints

* Undo `from_string` changes

* Update macos dependencies

* Apply updates to app-playground as well

* Update Wry + Tauri

* Update sysinfo

* Update theseus_gui to Rust 2024

* Downgrade rand in ariadne to fix labrinth

Labrinth can't use rand 0.9 due to argon2

* Cargo format

---------

Signed-off-by: Josiah Glosson <soujournme@gmail.com>
Co-authored-by: Prospector <prospectordev@gmail.com>
Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
Co-authored-by: Alejandro González <me@alegon.dev>
2025-05-02 09:51:17 +00:00

352 lines
12 KiB
Rust

#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
use native_dialog::{DialogBuilder, MessageLevel};
use std::env;
use tauri::{Listener, Manager};
use theseus::prelude::*;
mod api;
mod error;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "macos")]
#[macro_use]
extern crate cocoa;
#[cfg(target_os = "macos")]
#[macro_use]
extern crate objc;
// 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();
tracing::info!("Initializing app state...");
State::init().await?;
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"))]
{
State::init().await?;
}
tracing::info!("Finished checking for updates!");
let state = State::get().await?;
app.asset_protocol_scope()
.allow_directory(state.directories.caches_dir(), true)?;
app.asset_protocol_scope()
.allow_directory(state.directories.caches_dir().join("icons"), true)?;
Ok(())
}
// Should be call once Vue has mounted the app
#[tracing::instrument(skip_all)]
#[tauri::command]
fn show_window(app: tauri::AppHandle) {
let win = app.get_window("main").unwrap();
if let Err(e) = win.show() {
DialogBuilder::message()
.set_level(MessageLevel::Error)
.set_title("Initialization error")
.set_text(format!(
"Cannot display application window due to an error:\n{e}"
))
.alert()
.show()
.unwrap();
panic!("cannot display application window")
} else {
let _ = win.set_focus();
}
}
#[tauri::command]
fn is_dev() -> bool {
cfg!(debug_assertions)
}
// Toggles decorations
#[tauri::command]
async fn toggle_decorations(b: bool, window: tauri::Window) -> api::Result<()> {
window.set_decorations(b).map_err(|e| {
theseus::Error::from(theseus::ErrorKind::OtherError(format!(
"Failed to toggle decorations: {e}"
)))
})?;
Ok(())
}
#[tauri::command]
fn restart_app(app: tauri::AppHandle) {
app.restart();
}
// if Tauri app is called with arguments, then those arguments will be treated as commands
// ie: deep links or filepaths for .mrpacks
fn main() {
/*
tracing is set basd on the environment variable RUST_LOG=xxx, depending on the amount of logs to show
ERROR > WARN > INFO > DEBUG > TRACE
eg. RUST_LOG=info will show info, warn, and error logs
RUST_LOG="theseus=trace" will show *all* messages but from theseus only (and not dependencies using similar crates)
RUST_LOG="theseus=trace" will show *all* messages but from theseus only (and not dependencies using similar crates)
Error messages returned to Tauri will display as traced error logs if they return an error.
This will also include an attached span trace if the error is from a tracing error, and the level is set to info, debug, or trace
on unix:
RUST_LOG="theseus=trace" {run command}
*/
let _log_guard = theseus::start_logger();
tracing::info!("Initialized tracing subscriber. Loading Modrinth App!");
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) {
tracing::info!("Handling deep link from arg {payload}");
let payload = payload.clone();
tauri::async_runtime::spawn(api::utils::handle_command(
payload,
));
}
if let Some(win) = app.get_window("main") {
let _ = win.set_focus();
}
}))
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_opener::init())
.plugin(
tauri_plugin_window_state::Builder::default()
.with_filename("app-window-state.json")
.build(),
)
.setup(|app| {
#[cfg(target_os = "macos")]
{
let payload = macos::deep_link::get_or_init_payload(app);
let mtx_copy = payload.payload.clone();
app.listen("deep-link://new-url", move |url| {
let mtx_copy_copy = mtx_copy.clone();
let request = url.payload().to_owned();
let actual_request =
serde_json::from_str::<Vec<String>>(&request)
.ok()
.map(|mut x| x.remove(0))
.unwrap_or(request);
tauri::async_runtime::spawn(async move {
tracing::info!("Handling deep link {actual_request}");
let mut payload = mtx_copy_copy.lock().await;
if payload.is_none() {
*payload = Some(actual_request.clone());
}
let _ =
api::utils::handle_command(actual_request).await;
});
});
};
#[cfg(not(target_os = "macos"))]
app.listen("deep-link://new-url", |url| {
let payload = url.payload().to_owned();
tracing::info!("Handling deep link {payload}");
tauri::async_runtime::spawn(api::utils::handle_command(
payload,
));
dbg!(url);
});
#[cfg(not(target_os = "linux"))]
{
if let Some(window) = app.get_window("main") {
window.set_shadow(true).unwrap();
}
}
Ok(())
});
builder = builder
.plugin(api::auth::init())
.plugin(api::mr_auth::init())
.plugin(api::import::init())
.plugin(api::logs::init())
.plugin(api::jre::init())
.plugin(api::metadata::init())
.plugin(api::pack::init())
.plugin(api::process::init())
.plugin(api::profile::init())
.plugin(api::profile_create::init())
.plugin(api::settings::init())
.plugin(api::tags::init())
.plugin(api::utils::init())
.plugin(api::cache::init())
.plugin(api::ads::init())
.plugin(api::friends::init())
.plugin(api::worlds::init())
.invoke_handler(tauri::generate_handler![
initialize_state,
is_dev,
toggle_decorations,
show_window,
restart_app,
]);
#[cfg(target_os = "macos")]
{
builder = builder.plugin(macos::window_ext::init());
}
tracing::info!("Initializing app...");
let app = builder.build(tauri::generate_context!());
match app {
Ok(app) => {
#[allow(unused_variables)]
app.run(|app, event| {
#[cfg(target_os = "macos")]
if let tauri::RunEvent::Opened { urls } = event {
tracing::info!("Handling webview open {urls:?}");
let file = urls
.into_iter()
.filter_map(|url| url.to_file_path().ok())
.next();
if let Some(file) = file {
let payload =
macos::deep_link::get_or_init_payload(app);
let mtx_copy = payload.payload.clone();
let request = file.to_string_lossy().to_string();
tauri::async_runtime::spawn(async move {
let mut payload = mtx_copy.lock().await;
if payload.is_none() {
*payload = Some(request.clone());
}
let _ = api::utils::handle_command(request).await;
});
}
}
});
}
Err(e) => {
#[cfg(target_os = "windows")]
{
// tauri doesn't expose runtime errors, so matching a string representation seems like the only solution
if format!("{e:?}").contains(
"Runtime(CreateWebview(WebView2Error(WindowsError",
) {
DialogBuilder::message()
.set_level(MessageLevel::Error)
.set_title("Initialization error")
.set_text("Your Microsoft Edge WebView2 installation is corrupt.\n\nMicrosoft Edge WebView2 is required to run Modrinth App.\n\nLearn how to repair it at https://support.modrinth.com/en/articles/8797765-corrupted-microsoft-edge-webview2-installation")
.alert()
.show()
.unwrap();
panic!("webview2 initialization failed")
}
}
DialogBuilder::message()
.set_level(MessageLevel::Error)
.set_title("Initialization error")
.set_text(format!(
"Cannot initialize application due to an error:\n{e:?}"
))
.alert()
.show()
.unwrap();
tracing::error!("Error while running tauri application: {:?}", e);
panic!("{1}: {:?}", e, "error while running tauri application")
}
}
}