You've already forked AstralRinth
forked from didirus/AstralRinth
Misc settings (#137)
* Initial bug fixes * fix compile error on non-mac * Fix even more bugs * Fix more * fix more * fix build * fix build * Search fixes * Fix small instance ui * working basic * fix javaw issue * removed zip * working functions * merge fixes * fixed loadintg bar bug * menu fix * wait for settings to sync * safety expanded and for loading bars * swtiching to windows * minimize * default landing page * test link registry * url redirection * fix formatting * .mrpack windows * working mrpack reader * changed to one layer deep * working .mrpack + command handling for both opening and existing process * forge version numbers * working mac opening mrpack * reverted changes * prettier/fmt * missed debug statement * improvements + refactoring * renamed things to fit plugin * fixed bugs * removed println * overrides dont include mrpack * merge * fixes * fixes * fixed deletion * merge errors * force sync before export * removed testing * missed line * removed console log * mac error reverted * incoreclty named helper * additional fixes * added removed merges * fixed mislabled invokes * mac * added to new register method * comments, cleanup * mac clippy change * review changes * minor changes * moved create pack * removed playground compilation bug * fixed linux bug; other add ons * fixed review commets * cicd fix * mistaken import for prod * cicd fix --------- Co-authored-by: Jai A <jaiagr+gpg@pm.me>
This commit is contained in:
72
theseus/src/api/handler.rs
Normal file
72
theseus/src/api/handler.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::event::{
|
||||
emit::{emit_command, emit_warning},
|
||||
CommandPayload,
|
||||
};
|
||||
|
||||
/// Handles external functions (such as through URL deep linkage)
|
||||
/// Link is extracted value (link) in somewhat URL format, such as
|
||||
/// subdomain1/subdomain2
|
||||
/// (Does not include modrinth://)
|
||||
pub async fn handle_url(sublink: &str) -> crate::Result<CommandPayload> {
|
||||
Ok(match sublink.split_once('/') {
|
||||
// /mod/{id} - Installs a mod of mod id
|
||||
Some(("mod", id)) => CommandPayload::InstallMod { id: id.to_string() },
|
||||
// /version/{id} - Installs a specific version of id
|
||||
Some(("version", id)) => {
|
||||
CommandPayload::InstallVersion { id: id.to_string() }
|
||||
}
|
||||
// /modpack/{id} - Installs a modpack of modpack id
|
||||
Some(("modpack", id)) => {
|
||||
CommandPayload::InstallModpack { id: id.to_string() }
|
||||
}
|
||||
_ => {
|
||||
emit_warning(&format!(
|
||||
"Invalid command, unrecognized path: {sublink}"
|
||||
))
|
||||
.await?;
|
||||
return Err(crate::ErrorKind::InputError(format!(
|
||||
"Invalid command, unrecognized path: {sublink}"
|
||||
))
|
||||
.into());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn parse_command(
|
||||
command_string: &str,
|
||||
) -> crate::Result<CommandPayload> {
|
||||
tracing::debug!("Parsing command: {}", &command_string);
|
||||
|
||||
// modrinth://some-command
|
||||
// This occurs when following a web redirect link
|
||||
if let Some(sublink) = command_string.strip_prefix("modrinth://") {
|
||||
Ok(handle_url(sublink).await?)
|
||||
} else {
|
||||
// We assume anything else is a filepath to an .mrpack file
|
||||
let path = PathBuf::from(command_string);
|
||||
let path = path.canonicalize()?;
|
||||
if let Some(ext) = path.extension() {
|
||||
if ext == "mrpack" {
|
||||
return Ok(CommandPayload::RunMRPack { path });
|
||||
}
|
||||
}
|
||||
emit_warning(&format!(
|
||||
"Invalid command, unrecognized filetype: {}",
|
||||
path.display()
|
||||
))
|
||||
.await?;
|
||||
Err(crate::ErrorKind::InputError(format!(
|
||||
"Invalid command, unrecognized filetype: {}",
|
||||
path.display()
|
||||
))
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn parse_and_emit_command(command_string: &str) -> crate::Result<()> {
|
||||
let command = parse_command(command_string).await?;
|
||||
emit_command(command).await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
//! API for interacting with Theseus
|
||||
pub mod auth;
|
||||
pub mod handler;
|
||||
pub mod jre;
|
||||
pub mod logs;
|
||||
pub mod metadata;
|
||||
@@ -7,6 +8,7 @@ pub mod pack;
|
||||
pub mod process;
|
||||
pub mod profile;
|
||||
pub mod profile_create;
|
||||
pub mod safety;
|
||||
pub mod settings;
|
||||
pub mod tags;
|
||||
|
||||
@@ -22,6 +24,7 @@ pub mod prelude {
|
||||
pub use crate::{
|
||||
auth::{self, Credentials},
|
||||
data::*,
|
||||
event::CommandPayload,
|
||||
jre, metadata, pack, process,
|
||||
profile::{self, Profile},
|
||||
profile_create, settings,
|
||||
|
||||
5
theseus/src/api/safety.rs
Normal file
5
theseus/src/api/safety.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
use crate::state::{ProcessType, SafeProcesses};
|
||||
|
||||
pub async fn check_safe_loading_bars() -> crate::Result<bool> {
|
||||
SafeProcesses::is_complete(ProcessType::LoadingBar).await
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Theseus profile management interface
|
||||
|
||||
pub use crate::{
|
||||
state::{
|
||||
Hooks, JavaSettings, MemorySettings, Profile, Settings, WindowSize,
|
||||
|
||||
@@ -90,6 +90,10 @@ pub enum ErrorKind {
|
||||
|
||||
#[error("Error: {0}")]
|
||||
OtherError(String),
|
||||
|
||||
#[cfg(feature = "tauri")]
|
||||
#[error("Tauri error: {0}")]
|
||||
TauriError(#[from] tauri::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use super::LoadingBarId;
|
||||
use crate::event::{
|
||||
EventError, LoadingBar, LoadingBarType, ProcessPayloadType,
|
||||
ProfilePayloadType,
|
||||
use crate::{
|
||||
event::{
|
||||
CommandPayload, EventError, LoadingBar, LoadingBarType,
|
||||
ProcessPayloadType, ProfilePayloadType,
|
||||
},
|
||||
state::{ProcessType, SafeProcesses},
|
||||
};
|
||||
use futures::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use tracing::warn;
|
||||
|
||||
#[cfg(feature = "tauri")]
|
||||
use crate::event::{
|
||||
@@ -42,15 +44,29 @@ const CLI_PROGRESS_BAR_TOTAL: u64 = 1000;
|
||||
}
|
||||
*/
|
||||
|
||||
// Initialize a loading bar for use in emit_loading
|
||||
// This will generate a LoadingBarId, which is used to refer to the loading bar uniquely.
|
||||
// total is the total amount of work to be done- all emissions will be considered a fraction of this value (should be 1 or 100 for simplicity)
|
||||
// title is the title of the loading bar
|
||||
/// Initialize a loading bar for use in emit_loading
|
||||
/// This will generate a LoadingBarId, which is used to refer to the loading bar uniquely.
|
||||
/// total is the total amount of work to be done- all emissions will be considered a fraction of this value (should be 1 or 100 for simplicity)
|
||||
/// title is the title of the loading bar
|
||||
/// The app will wait for this loading bar to finish before exiting, as it is considered safe.
|
||||
#[theseus_macros::debug_pin]
|
||||
pub async fn init_loading(
|
||||
bar_type: LoadingBarType,
|
||||
total: f64,
|
||||
title: &str,
|
||||
) -> crate::Result<LoadingBarId> {
|
||||
let key = init_loading_unsafe(bar_type, total, title).await?;
|
||||
SafeProcesses::add_uuid(ProcessType::LoadingBar, key.0).await?;
|
||||
Ok(key)
|
||||
}
|
||||
|
||||
/// An unsafe loading bar can be created without adding it to the SafeProcesses list,
|
||||
/// meaning that the app won't ask to wait for it to finish before exiting.
|
||||
#[theseus_macros::debug_pin]
|
||||
pub async fn init_loading_unsafe(
|
||||
bar_type: LoadingBarType,
|
||||
total: f64,
|
||||
title: &str,
|
||||
) -> crate::Result<LoadingBarId> {
|
||||
let event_state = crate::EventState::get().await?;
|
||||
let key = LoadingBarId(Uuid::new_v4());
|
||||
@@ -76,8 +92,6 @@ pub async fn init_loading(
|
||||
).unwrap()
|
||||
.progress_chars("#>-"),
|
||||
);
|
||||
//pb.set_message(title);
|
||||
|
||||
pb
|
||||
},
|
||||
},
|
||||
@@ -215,7 +229,25 @@ pub async fn emit_warning(message: &str) -> crate::Result<()> {
|
||||
)
|
||||
.map_err(EventError::from)?;
|
||||
}
|
||||
warn!("{}", message);
|
||||
tracing::warn!("{}", message);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// emit_command(CommandPayload::Something { something })
|
||||
// ie: installing a pack, opening an .mrpack, etc
|
||||
// Generally used for url deep links and file opens that we we want to handle in the frontend
|
||||
#[allow(dead_code)]
|
||||
#[allow(unused_variables)]
|
||||
pub async fn emit_command(command: CommandPayload) -> crate::Result<()> {
|
||||
tracing::debug!("Command: {}", serde_json::to_string(&command)?);
|
||||
#[cfg(feature = "tauri")]
|
||||
{
|
||||
let event_state = crate::EventState::get().await?;
|
||||
event_state
|
||||
.app
|
||||
.emit_all("command", command)
|
||||
.map_err(EventError::from)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ use tokio::sync::OnceCell;
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::state::SafeProcesses;
|
||||
|
||||
pub mod emit;
|
||||
|
||||
// Global event state
|
||||
@@ -48,6 +50,12 @@ impl EventState {
|
||||
Ok(EVENT_STATE.get().ok_or(EventError::NotInitialized)?.clone())
|
||||
}
|
||||
|
||||
// Initialization requires no app handle in non-tauri mode, so we can just use the same function
|
||||
#[cfg(not(feature = "tauri"))]
|
||||
pub async fn get() -> crate::Result<Arc<Self>> {
|
||||
Self::init().await
|
||||
}
|
||||
|
||||
// Values provided should not be used directly, as they are clones and are not guaranteed to be up-to-date
|
||||
pub async fn list_progress_bars() -> crate::Result<HashMap<Uuid, LoadingBar>>
|
||||
{
|
||||
@@ -62,10 +70,11 @@ impl EventState {
|
||||
Ok(display_list)
|
||||
}
|
||||
|
||||
// Initialization requires no app handle in non-tauri mode, so we can just use the same function
|
||||
#[cfg(not(feature = "tauri"))]
|
||||
pub async fn get() -> crate::Result<Arc<Self>> {
|
||||
Self::init().await
|
||||
#[cfg(feature = "tauri")]
|
||||
pub async fn get_main_window() -> crate::Result<Option<tauri::Window>> {
|
||||
use tauri::Manager;
|
||||
let value = Self::get().await?;
|
||||
Ok(value.app.get_window("main"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +100,6 @@ pub struct LoadingBarId(Uuid);
|
||||
impl Drop for LoadingBarId {
|
||||
fn drop(&mut self) {
|
||||
let loader_uuid = self.0;
|
||||
let _event = LoadingBarType::StateInit;
|
||||
let _message = "finished".to_string();
|
||||
tokio::spawn(async move {
|
||||
if let Ok(event_state) = EventState::get().await {
|
||||
let mut bars = event_state.loading_bars.write().await;
|
||||
@@ -132,6 +139,11 @@ impl Drop for LoadingBarId {
|
||||
#[cfg(not(any(feature = "tauri", feature = "cli")))]
|
||||
bars.remove(&loader_uuid);
|
||||
}
|
||||
let _ = SafeProcesses::complete(
|
||||
crate::state::ProcessType::LoadingBar,
|
||||
loader_uuid,
|
||||
)
|
||||
.await;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -184,6 +196,24 @@ pub struct WarningPayload {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
#[serde(tag = "event")]
|
||||
pub enum CommandPayload {
|
||||
InstallMod {
|
||||
id: String,
|
||||
},
|
||||
InstallVersion {
|
||||
id: String,
|
||||
},
|
||||
InstallModpack {
|
||||
id: String,
|
||||
},
|
||||
RunMRPack {
|
||||
// run or install .mrpack
|
||||
path: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct ProcessPayload {
|
||||
pub uuid: Uuid, // processes in state are going to be identified by UUIDs, as they might change to different processes
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::event::{LoadingBarId, LoadingBarType};
|
||||
use crate::jre::{JAVA_17_KEY, JAVA_18PLUS_KEY, JAVA_8_KEY};
|
||||
use crate::prelude::JavaVersion;
|
||||
use crate::state::ProfileInstallStage;
|
||||
use crate::EventState;
|
||||
use crate::{
|
||||
process,
|
||||
state::{self as st, MinecraftChild},
|
||||
@@ -471,6 +472,18 @@ pub async fn launch_minecraft(
|
||||
"{MINECRAFT_UUID}".to_string(),
|
||||
);
|
||||
|
||||
// If in tauri, and the 'minimize on launch' setting is enabled, minimize the window
|
||||
#[cfg(feature = "tauri")]
|
||||
{
|
||||
let window = EventState::get_main_window().await?;
|
||||
if let Some(window) = window {
|
||||
let settings = state.settings.read().await;
|
||||
if settings.hide_on_process {
|
||||
window.minimize()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create Minecraft child by inserting it into the state
|
||||
// This also spawns the process and prepares the subsequent processes
|
||||
let mut state_children = state.children.write().await;
|
||||
|
||||
@@ -15,9 +15,11 @@ mod config;
|
||||
mod error;
|
||||
mod event;
|
||||
mod launcher;
|
||||
mod logger;
|
||||
mod state;
|
||||
|
||||
pub use api::*;
|
||||
pub use error::*;
|
||||
pub use event::{EventState, LoadingBar, LoadingBarType};
|
||||
pub use logger::start_logger;
|
||||
pub use state::State;
|
||||
|
||||
75
theseus/src/logger.rs
Normal file
75
theseus/src/logger.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
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}
|
||||
|
||||
The default is theseus=show, meaning only logs from theseus will be displayed, and at the info or higher level.
|
||||
|
||||
*/
|
||||
|
||||
use tracing_appender::non_blocking::WorkerGuard;
|
||||
|
||||
// Handling for the live development logging
|
||||
// This will log to the console, and will not log to a file
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn start_logger() -> Option<WorkerGuard> {
|
||||
use tracing_subscriber::prelude::*;
|
||||
|
||||
let filter = tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("theseus=info"));
|
||||
let subscriber = 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");
|
||||
None
|
||||
}
|
||||
|
||||
// Handling for the live production logging
|
||||
// This will log to a file in the logs directory, and will not show any logs in the console
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub fn start_logger() -> Option<WorkerGuard> {
|
||||
use crate::prelude::DirectoryInfo;
|
||||
use tracing_appender::rolling::{RollingFileAppender, Rotation};
|
||||
use tracing_subscriber::fmt::time::ChronoLocal;
|
||||
use tracing_subscriber::prelude::*;
|
||||
|
||||
// Initialize and get logs directory path
|
||||
let path = if let Some(dir) = DirectoryInfo::init().ok() {
|
||||
dir.launcher_logs_dir()
|
||||
} else {
|
||||
eprintln!("Could not create logger.");
|
||||
return None;
|
||||
};
|
||||
|
||||
let filter = tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("theseus=info"));
|
||||
|
||||
let file_appender =
|
||||
RollingFileAppender::new(Rotation::DAILY, path, "theseus.log");
|
||||
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
|
||||
|
||||
let subscriber = tracing_subscriber::registry()
|
||||
.with(
|
||||
tracing_subscriber::fmt::layer()
|
||||
.with_writer(non_blocking)
|
||||
.with_ansi(false) // disable ANSI escape codes
|
||||
.with_timer(ChronoLocal::rfc3339()),
|
||||
)
|
||||
.with(filter)
|
||||
.with(tracing_error::ErrorLayer::default());
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber)
|
||||
.expect("Setting default subscriber failed");
|
||||
|
||||
Some(guard)
|
||||
}
|
||||
@@ -12,6 +12,7 @@ use tracing::error;
|
||||
|
||||
use crate::event::emit::emit_process;
|
||||
use crate::event::ProcessPayloadType;
|
||||
use crate::EventState;
|
||||
use tokio::task::JoinHandle;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -129,6 +130,16 @@ impl Children {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If in tauri, window should show itself again after process exists if it was hidden
|
||||
#[cfg(feature = "tauri")]
|
||||
{
|
||||
let window = EventState::get_main_window().await?;
|
||||
if let Some(window) = window {
|
||||
window.unminimize()?;
|
||||
}
|
||||
}
|
||||
|
||||
if !mc_exit_status.success() {
|
||||
emit_process(
|
||||
uuid,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Theseus directory information
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use tokio::fs;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DirectoryInfo {
|
||||
@@ -11,7 +11,7 @@ pub struct DirectoryInfo {
|
||||
impl DirectoryInfo {
|
||||
/// Get all paths needed for Theseus to operate properly
|
||||
#[tracing::instrument]
|
||||
pub async fn init() -> crate::Result<Self> {
|
||||
pub fn init() -> crate::Result<Self> {
|
||||
// Working directory
|
||||
let working_dir = std::env::current_dir().map_err(|err| {
|
||||
crate::ErrorKind::FSError(format!(
|
||||
@@ -26,7 +26,7 @@ impl DirectoryInfo {
|
||||
"Could not find valid config dir".to_string(),
|
||||
))?;
|
||||
|
||||
fs::create_dir_all(&config_dir).await.map_err(|err| {
|
||||
fs::create_dir_all(&config_dir).map_err(|err| {
|
||||
crate::ErrorKind::FSError(format!(
|
||||
"Error creating Theseus config directory: {err}"
|
||||
))
|
||||
@@ -130,6 +130,11 @@ impl DirectoryInfo {
|
||||
.join("modrinth_logs")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn launcher_logs_dir(&self) -> PathBuf {
|
||||
self.config_dir.join("launcher_logs")
|
||||
}
|
||||
|
||||
/// Get the file containing the global database
|
||||
#[inline]
|
||||
pub fn database_file(&self) -> PathBuf {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
//! Theseus state management system
|
||||
use crate::event::emit::emit_loading;
|
||||
use crate::event::emit::{emit_loading, init_loading_unsafe};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::event::emit::init_loading;
|
||||
use crate::event::LoadingBarType;
|
||||
use crate::loading_join;
|
||||
|
||||
@@ -46,6 +45,9 @@ pub use self::tags::*;
|
||||
mod java_globals;
|
||||
pub use self::java_globals::*;
|
||||
|
||||
mod safe_processes;
|
||||
pub use self::safe_processes::*;
|
||||
|
||||
// Global state
|
||||
static LAUNCHER_STATE: OnceCell<Arc<State>> = OnceCell::const_new();
|
||||
pub struct State {
|
||||
@@ -75,6 +77,8 @@ pub struct State {
|
||||
pub(crate) users: RwLock<Users>,
|
||||
/// Launcher tags
|
||||
pub(crate) tags: RwLock<Tags>,
|
||||
/// Launcher processes that should be safely exited on shutdown
|
||||
pub(crate) safety_processes: RwLock<SafeProcesses>,
|
||||
|
||||
/// File watcher debouncer
|
||||
pub(crate) file_watcher: RwLock<Debouncer<RecommendedWatcher>>,
|
||||
@@ -88,7 +92,7 @@ impl State {
|
||||
LAUNCHER_STATE
|
||||
.get_or_try_init(|| {
|
||||
async {
|
||||
let loading_bar = init_loading(
|
||||
let loading_bar = init_loading_unsafe(
|
||||
LoadingBarType::StateInit,
|
||||
100.0,
|
||||
"Initializing launcher",
|
||||
@@ -97,7 +101,7 @@ impl State {
|
||||
|
||||
let mut file_watcher = init_watcher().await?;
|
||||
|
||||
let directories = DirectoryInfo::init().await?;
|
||||
let directories = DirectoryInfo::init()?;
|
||||
emit_loading(&loading_bar, 10.0, None).await?;
|
||||
|
||||
// Settings
|
||||
@@ -132,6 +136,7 @@ impl State {
|
||||
|
||||
let children = Children::new();
|
||||
let auth_flow = AuthTask::new();
|
||||
let safety_processes = SafeProcesses::new();
|
||||
emit_loading(&loading_bar, 10.0, None).await?;
|
||||
|
||||
Ok(Arc::new(Self {
|
||||
@@ -151,6 +156,7 @@ impl State {
|
||||
children: RwLock::new(children),
|
||||
auth_flow: RwLock::new(auth_flow),
|
||||
tags: RwLock::new(tags),
|
||||
safety_processes: RwLock::new(safety_processes),
|
||||
file_watcher: RwLock::new(file_watcher),
|
||||
}))
|
||||
}
|
||||
|
||||
69
theseus/src/state/safe_processes.rs
Normal file
69
theseus/src/state/safe_processes.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::State;
|
||||
|
||||
// We implement a store for safe loading bars such that we can wait for them to complete
|
||||
// We create this store separately from the loading bars themselves, because this may be extended as needed
|
||||
pub struct SafeProcesses {
|
||||
pub loading_bars: Vec<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum ProcessType {
|
||||
LoadingBar,
|
||||
// Potentially other types of processes (ie: IO operations?)
|
||||
}
|
||||
|
||||
impl SafeProcesses {
|
||||
// init
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
loading_bars: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a new running safe process to the list by uuid
|
||||
pub async fn add_uuid(
|
||||
r#type: ProcessType,
|
||||
uuid: Uuid,
|
||||
) -> crate::Result<Uuid> {
|
||||
let state = State::get().await?;
|
||||
let mut safe_processes = state.safety_processes.write().await;
|
||||
match r#type {
|
||||
ProcessType::LoadingBar => {
|
||||
safe_processes.loading_bars.push(uuid);
|
||||
}
|
||||
}
|
||||
Ok(uuid)
|
||||
}
|
||||
|
||||
// Mark a safe process as finishing
|
||||
pub async fn complete(
|
||||
r#type: ProcessType,
|
||||
uuid: Uuid,
|
||||
) -> crate::Result<()> {
|
||||
let state = State::get().await?;
|
||||
let mut safe_processes = state.safety_processes.write().await;
|
||||
|
||||
match r#type {
|
||||
ProcessType::LoadingBar => {
|
||||
safe_processes.loading_bars.retain(|x| *x != uuid);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Check if there are any pending safe processes of a given type
|
||||
pub async fn is_complete(r#type: ProcessType) -> crate::Result<bool> {
|
||||
let state = State::get().await?;
|
||||
let safe_processes = state.safety_processes.read().await;
|
||||
match r#type {
|
||||
ProcessType::LoadingBar => {
|
||||
if safe_processes.loading_bars.is_empty() {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,10 @@ pub struct Settings {
|
||||
pub version: u32,
|
||||
pub collapsed_navigation: bool,
|
||||
#[serde(default)]
|
||||
pub hide_on_process: bool,
|
||||
#[serde(default)]
|
||||
pub default_page: DefaultPage,
|
||||
#[serde(default)]
|
||||
pub developer_mode: bool,
|
||||
#[serde(default)]
|
||||
pub opt_out_analytics: bool,
|
||||
@@ -54,6 +58,8 @@ impl Default for Settings {
|
||||
max_concurrent_writes: 10,
|
||||
version: CURRENT_FORMAT_VERSION,
|
||||
collapsed_navigation: false,
|
||||
hide_on_process: false,
|
||||
default_page: DefaultPage::Home,
|
||||
developer_mode: false,
|
||||
opt_out_analytics: false,
|
||||
advanced_rendering: true,
|
||||
@@ -126,7 +132,8 @@ impl Settings {
|
||||
"Error saving settings to file: {err}"
|
||||
))
|
||||
.as_error()
|
||||
})
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,3 +179,16 @@ pub struct Hooks {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub post_exit: Option<String>,
|
||||
}
|
||||
|
||||
/// Opening window to start with
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
||||
pub enum DefaultPage {
|
||||
Home,
|
||||
Library,
|
||||
}
|
||||
|
||||
impl Default for DefaultPage {
|
||||
fn default() -> Self {
|
||||
Self::Home
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user