OneLauncher-Cracked/packages/oneclient_app/src/view/app/clusters/instance/create.rs

246 lines
7.9 KiB
Rust

use std::collections::HashSet;
use freya::prelude::*;
use freya::router::RouterContext;
use oneclient_core::GameVersionKind;
use oneclient_core::clusters::{ClusterKind, ModpackSource};
use super::copy::{footer_note, heading, is_ready, step_value};
use super::data::{Picks, resolve};
use super::details::DetailsState;
use super::model::*;
use super::rail::{Rail, RowState, rail, steps_card, version_art};
use super::shell::{Shell, shell};
use super::steps;
use crate::Route;
use crate::hooks::{
ClusterAction, settled_or_loading, use_active_cluster_id, use_cluster_mutation, use_clusters,
use_dispatch,
};
use crate::utils::default_cluster;
const MODPACK_BROWSE_TYPE: &str = "modpack";
const MODPACK_EXTENSIONS: [&str; 2] = ["mrpack", "zip"];
fn create_action(wizard: Wizard, picks: &Picks) -> Option<ClusterAction> {
let mc_version = picks.versions.chosen.clone()?;
let mc_loader = picks.loader.chosen?;
Some(ClusterAction::CreateInstance {
kind: picks.kind,
name: picks.name.trim().to_string(),
mc_version,
mc_loader,
mc_loader_version: picks.loader.version.clone(),
description: wizard.details.description_value(),
tags: wizard.details.tags.read().clone(),
cover_source: wizard.details.cover.read().clone(),
bundles: (picks.kind == ClusterKind::OneClient && picks.bundles.loaded)
.then(|| picks.bundles.taken()),
})
}
fn wizard_rail(wizard: Wizard, picks: &Picks) -> Element {
let description = wizard.details.description.read().trim().to_string();
let tags = wizard.details.tags.read().clone();
let title = if picks.name.trim().is_empty() {
"New instance".to_string()
} else {
picks.name.clone()
};
let subtitle = if description.is_empty() {
match picks.choice {
TypeChoice::OneClient => match &picks.versions.chosen {
Some(version) => format!("OneClient · {version}"),
None => "OneClient".to_string(),
},
TypeChoice::Scratch => match &picks.versions.chosen {
Some(version) => format!("{version} · {}", picks.loader_label()),
None => picks.loader_label(),
},
TypeChoice::Modpack => "Modpack".to_string(),
}
} else {
description
};
let rows = picks
.steps
.iter()
.enumerate()
.map(|(index, step)| {
let state = if index < picks.index {
RowState::Done
} else if index == picks.index {
RowState::Current
} else {
RowState::Pending
};
let value = if state == RowState::Pending {
String::new()
} else {
step_value(wizard, picks, *step)
};
(step.label(), value, state)
})
.collect();
let art = if picks.choice == TypeChoice::Modpack {
version_art(None, None)
} else {
version_art(picks.versions.chosen.as_deref(), picks.loader.chosen)
}
.picked_cover(wizard.details.cover.read().clone());
rail(Rail {
art,
title,
subtitle,
card: steps_card(rows),
tags: if picks.step == Step::Customize {
tags
} else {
Vec::new()
},
})
}
#[derive(PartialEq)]
pub struct CreateInstanceModal {
on_close: EventHandler<()>,
}
impl CreateInstanceModal {
pub fn new(on_close: impl Into<EventHandler<()>>) -> Self {
Self {
on_close: on_close.into(),
}
}
}
impl Component for CreateInstanceModal {
fn render(&self) -> impl IntoElement {
let mutation = use_cluster_mutation();
let wizard = Wizard {
step: use_state(|| 0usize),
choice: use_state(|| TypeChoice::OneClient),
version: use_state(|| None::<String>),
filter: use_state(|| kind_bit(GameVersionKind::Release)),
query: use_state(String::new),
loader: use_state(|| LoaderChoice::Fabric),
loader_version: use_state(|| None::<String>),
declined: use_state(|| None::<HashSet<String>>),
modpack_origin: use_state(|| ModpackOrigin::Browse),
details: DetailsState::blank(),
};
let dispatch = use_dispatch();
let browse_cluster = browse_cluster();
let picks = resolve(wizard);
let action = create_action(wizard, &picks);
let (title, subtitle) = heading(&picks);
let first = picks.index == 0;
let last = picks.step == Step::Customize;
let modpack_step = picks.step == Step::Modpack;
let origin = picks.modpack_origin;
let index = picks.index;
let mut step = wizard.step;
let mut query = wizard.query;
let close_x = self.on_close.clone();
let close_cancel = self.on_close.clone();
let close_created = self.on_close.clone();
let close_modpack = self.on_close.clone();
shell(Shell {
rail: wizard_rail(wizard, &picks),
eyebrow: None,
title: title.to_string(),
subtitle,
body: steps::body(wizard, &picks),
scrolls_itself: picks.step == Step::Version,
note: footer_note(&picks),
secondary_label: if first { "Cancel" } else { "Back" }.to_string(),
primary_label: match (modpack_step, origin) {
(true, ModpackOrigin::Browse) => "Browse modpacks",
(true, ModpackOrigin::File) => "Choose file",
(false, _) if last => "Create instance",
(false, _) => "Next",
}
.to_string(),
primary_enabled: is_ready(&picks),
on_close: (move |()| close_x.call(())).into(),
on_secondary: (move |()| {
if first {
close_cancel.call(());
} else {
step.set(index.saturating_sub(1));
}
})
.into(),
on_primary: (move |()| {
if modpack_step {
start_modpack(
origin,
browse_cluster,
dispatch.clone(),
close_modpack.clone(),
);
} else if last {
if let Some(action) = action.clone() {
mutation.mutate(action);
close_created.call(());
}
} else {
query.set(String::new());
step.set(index + 1);
}
})
.into(),
})
}
}
fn browse_cluster() -> i64 {
let clusters = settled_or_loading(&use_clusters()).unwrap_or_default();
let active = *use_active_cluster_id().read();
default_cluster(clusters, active)
.map(|cluster| cluster.id)
.unwrap_or_default()
}
fn start_modpack(
origin: ModpackOrigin,
cluster_id: i64,
dispatch: crate::Actions,
on_close: EventHandler<()>,
) {
match origin {
ModpackOrigin::Browse => {
on_close.call(());
let _ = RouterContext::get().push(Route::Browser {
cluster_id,
package_type: MODPACK_BROWSE_TYPE.to_string(),
pick_cluster: true,
});
}
ModpackOrigin::File => {
spawn(async move {
let Some(handle) = rfd::AsyncFileDialog::new()
.set_title("Choose a modpack")
.add_filter("Modpack", &MODPACK_EXTENSIONS)
.pick_file()
.await
else {
return;
};
dispatch.install_modpack(ModpackSource::File(handle.path().to_path_buf()));
on_close.call(());
});
}
}
}