Skip to content

Commit 0af60d8

Browse files
authored
Merge pull request #30 from Umatriz/client-refactor
Client refactor
2 parents cf54f90 + 90a0024 commit 0af60d8

39 files changed

+2006
-1177
lines changed

Cargo.lock

+264-240
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/client/Cargo.toml

+8-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@ tracing-subscriber.workspace = true
1717
nomi-core = { path = "../nomi-core"}
1818

1919
eframe = "0.27.2"
20+
egui_dock = "0.12"
21+
egui_form = { version = "0.1.1", features = ["validator_garde"] }
22+
egui-file-dialog = "0.5.0"
23+
egui_tracing = "0.2.2"
24+
egui_extras = "0.27.2"
25+
garde = { version = "0.18.0", features = ["derive", "regex"] }
26+
2027
pollster = "0.3.0"
2128
toml = "0.8.0"
22-
rfd = "0.14.1"
2329
tracing-appender = "0.2.2"
24-
egui_tracing = "0.2.2"
30+
once_cell = "1.19.0"

crates/client/src/channel.rs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use std::ops::{Deref, DerefMut};
2+
3+
use tokio::sync::mpsc::{Receiver, Sender};
4+
5+
pub struct Channel<T> {
6+
tx: Sender<T>,
7+
rx: Receiver<T>,
8+
}
9+
10+
impl<T> Channel<T> {
11+
pub fn new(buffer: usize) -> Self {
12+
let (tx, rx) = tokio::sync::mpsc::channel(buffer);
13+
Self { tx, rx }
14+
}
15+
16+
pub fn clone_tx(&self) -> Sender<T> {
17+
self.tx.clone()
18+
}
19+
}
20+
21+
impl<T> Deref for Channel<T> {
22+
type Target = Receiver<T>;
23+
24+
fn deref(&self) -> &Self::Target {
25+
&self.rx
26+
}
27+
}
28+
29+
impl<T> DerefMut for Channel<T> {
30+
fn deref_mut(&mut self) -> &mut Self::Target {
31+
&mut self.rx
32+
}
33+
}

crates/client/src/client_settings.rs

-19
This file was deleted.

crates/client/src/components.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use eframe::egui::Ui;
2+
3+
pub mod add_profile_menu;
4+
pub mod add_tab_menu;
5+
pub mod download_progress;
6+
pub mod profiles;
7+
pub mod settings;
8+
9+
pub trait Component: Sized {
10+
fn ui(self, ui: &mut Ui);
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
use eframe::egui::{self, Color32, RichText};
2+
use nomi_core::{
3+
configs::profile::{Loader, ProfileState, VersionProfile},
4+
repository::{
5+
fabric_meta::{get_fabric_versions, FabricVersions},
6+
launcher_manifest::{LauncherManifest, Version},
7+
manifest::VersionType,
8+
},
9+
};
10+
11+
use crate::errors_pool::ErrorPoolExt;
12+
13+
use super::{download_progress::Task, profiles::ProfilesState, Component};
14+
15+
pub struct AddProfileMenu<'a> {
16+
pub launcher_manifest: &'a LauncherManifest,
17+
pub menu_state: &'a mut AddProfileMenuState,
18+
pub profiles_state: &'a mut ProfilesState,
19+
}
20+
21+
pub struct AddProfileMenuState {
22+
selected_version_type: VersionType,
23+
24+
profile_name_buf: String,
25+
selected_version_buf: Option<Version>,
26+
selected_loader_buf: Loader,
27+
28+
task: Option<Task<FabricVersions>>,
29+
fabric_versions: FabricVersions,
30+
}
31+
32+
impl AddProfileMenuState {
33+
/// It will request available versions no matter which `Loader`
34+
/// is selected
35+
pub fn request_fabric_versions(&mut self) {
36+
if self.task.is_none() {
37+
self.task = Some(Task::new("Requesting available Fabric versions".to_owned()));
38+
}
39+
40+
if let Some(task) = self.task.as_mut() {
41+
task.set_running(true)
42+
}
43+
44+
let version = self.selected_version_buf.as_ref().unwrap().id.clone();
45+
46+
let sender = self.task.as_ref().unwrap().result_channel().clone_tx();
47+
48+
tokio::spawn(async move {
49+
if let Some(versions) = get_fabric_versions(version).await.report_error() {
50+
sender.send(versions).await.report_error();
51+
}
52+
});
53+
}
54+
}
55+
56+
impl Default for AddProfileMenuState {
57+
fn default() -> Self {
58+
Self::default_const()
59+
}
60+
}
61+
62+
impl AddProfileMenuState {
63+
pub const fn default_const() -> Self {
64+
Self {
65+
selected_version_type: VersionType::Release,
66+
67+
profile_name_buf: String::new(),
68+
selected_version_buf: None,
69+
selected_loader_buf: Loader::Vanilla,
70+
task: None,
71+
fabric_versions: Vec::new(),
72+
}
73+
}
74+
}
75+
76+
impl Component for AddProfileMenu<'_> {
77+
fn ui(self, ui: &mut eframe::egui::Ui) {
78+
fn fabric_version_is(
79+
selected_loader: &Loader,
80+
loader_is: impl Fn(Loader) -> bool,
81+
func: impl Fn(Option<&String>) -> bool,
82+
) -> bool {
83+
loader_is(Loader::Fabric { version: None })
84+
&& match selected_loader {
85+
Loader::Fabric { version } => func(version.as_ref()),
86+
Loader::Vanilla => unreachable!(),
87+
}
88+
}
89+
90+
if let Some(Ok(versiosn)) = self
91+
.menu_state
92+
.task
93+
.as_mut()
94+
.map(|task| task.result_channel_mut())
95+
.map(|channel| channel.try_recv())
96+
{
97+
self.menu_state.fabric_versions = versiosn;
98+
if let Some(task) = self.menu_state.task.as_mut() {
99+
task.set_running(false)
100+
}
101+
}
102+
103+
{
104+
ui.label("Profile name:");
105+
ui.text_edit_singleline(&mut self.menu_state.profile_name_buf);
106+
107+
egui::ComboBox::from_label("Versions Filter")
108+
.selected_text(format!("{:?}", self.menu_state.selected_version_type))
109+
.show_ui(ui, |ui| {
110+
ui.selectable_value(
111+
&mut self.menu_state.selected_version_type,
112+
VersionType::Release,
113+
"Release",
114+
);
115+
ui.selectable_value(
116+
&mut self.menu_state.selected_version_type,
117+
VersionType::Snapshot,
118+
"Snapshot",
119+
);
120+
});
121+
122+
let versions_iter = self.launcher_manifest.versions.iter();
123+
let versions = match self.menu_state.selected_version_type {
124+
VersionType::Release => versions_iter
125+
.filter(|v| v.version_type == "release")
126+
.collect::<Vec<_>>(),
127+
VersionType::Snapshot => versions_iter
128+
.filter(|v| v.version_type == "snapshot")
129+
.collect::<Vec<_>>(),
130+
};
131+
132+
egui::ComboBox::from_label("Select version")
133+
.selected_text(
134+
self.menu_state
135+
.selected_version_buf
136+
.as_ref()
137+
.map_or("No version selcted", |v| &v.id),
138+
)
139+
.show_ui(ui, |ui| {
140+
for version in versions {
141+
let value = ui.selectable_value(
142+
&mut self.menu_state.selected_version_buf.as_ref(),
143+
Some(version),
144+
&version.id,
145+
);
146+
if value.clicked() {
147+
self.menu_state.selected_version_buf = Some(version.clone());
148+
if matches!(self.menu_state.selected_loader_buf, Loader::Fabric { .. })
149+
{
150+
self.menu_state.request_fabric_versions()
151+
}
152+
}
153+
}
154+
});
155+
156+
ui.add_enabled_ui(self.menu_state.selected_version_buf.is_some(), |ui| {
157+
egui::ComboBox::from_label("Select the loader")
158+
.selected_text(format!("{:?}", self.menu_state.selected_loader_buf))
159+
.show_ui(ui, |ui| {
160+
ui.selectable_value(
161+
&mut self.menu_state.selected_loader_buf,
162+
Loader::Vanilla,
163+
"Vanilla",
164+
);
165+
let fabric = ui.selectable_value(
166+
&mut self.menu_state.selected_loader_buf,
167+
Loader::Fabric { version: None },
168+
"Fabric",
169+
);
170+
171+
if fabric.clicked() {
172+
println!("Test!");
173+
self.menu_state.request_fabric_versions();
174+
}
175+
});
176+
});
177+
178+
if matches!(self.menu_state.selected_loader_buf, Loader::Fabric { .. }) {
179+
ui.label(
180+
RichText::new("Warn: Fabric version will not run if you have not installed Vanilla version previously")
181+
.color(ui.visuals().warn_fg_color),
182+
);
183+
184+
if !self.menu_state.fabric_versions.is_empty() {
185+
if let Loader::Fabric { version } = &mut self.menu_state.selected_loader_buf {
186+
egui::ComboBox::from_label("Select Fabric version")
187+
.selected_text(version.as_deref().unwrap_or("No version selected"))
188+
.show_ui(ui, |ui| {
189+
for fabric_version in &self.menu_state.fabric_versions {
190+
let stability_text = match fabric_version.loader.stable {
191+
true => "stable",
192+
false => "unstable",
193+
};
194+
195+
let stability_color = match fabric_version.loader.stable {
196+
true => Color32::GREEN,
197+
false => ui.visuals().warn_fg_color,
198+
};
199+
ui.horizontal(|ui| {
200+
ui.selectable_value(
201+
version,
202+
Some(fabric_version.loader.version.clone()),
203+
RichText::new(&fabric_version.loader.version)
204+
.color(stability_color),
205+
);
206+
ui.label(RichText::new("❓").color(stability_color))
207+
.on_hover_text(stability_text);
208+
});
209+
}
210+
});
211+
}
212+
} else if self
213+
.menu_state
214+
.task
215+
.as_ref()
216+
.is_some_and(|task| task.is_running())
217+
{
218+
ui.horizontal(|ui| {
219+
ui.spinner();
220+
ui.label("Requesting available Fabric versions");
221+
});
222+
} else {
223+
ui.label(
224+
RichText::new("Fabric is not available for this version")
225+
.color(ui.visuals().error_fg_color),
226+
);
227+
}
228+
}
229+
}
230+
231+
let some_version_buf = || self.menu_state.selected_version_buf.is_some();
232+
let loader_is = |kind| self.menu_state.selected_loader_buf == kind;
233+
234+
let fabric_version_is_some = || {
235+
fabric_version_is(
236+
&self.menu_state.selected_loader_buf,
237+
loader_is,
238+
|opt: Option<&String>| opt.is_some(),
239+
)
240+
};
241+
let fabric_version_is_none = || {
242+
fabric_version_is(
243+
&self.menu_state.selected_loader_buf,
244+
loader_is,
245+
|opt: Option<&String>| opt.is_none(),
246+
)
247+
};
248+
249+
let fabric_versions_non_empty = || !self.menu_state.fabric_versions.is_empty();
250+
251+
if self.menu_state.selected_version_buf.is_none() {
252+
ui.label(
253+
RichText::new("You must select the version").color(ui.visuals().error_fg_color),
254+
);
255+
}
256+
257+
if fabric_version_is_none() {
258+
ui.label(
259+
RichText::new("You must select the Fabric Version")
260+
.color(ui.visuals().error_fg_color),
261+
);
262+
}
263+
264+
if ui
265+
.add_enabled(
266+
some_version_buf()
267+
&& (loader_is(Loader::Vanilla)
268+
|| (fabric_version_is_some() && fabric_versions_non_empty())),
269+
egui::Button::new("Create"),
270+
)
271+
.clicked()
272+
{
273+
self.profiles_state.add_profile(VersionProfile {
274+
id: self.profiles_state.create_id(),
275+
name: self.menu_state.profile_name_buf.clone(),
276+
state: ProfileState::NotDownloaded {
277+
// PANICS: It will never panic because it's
278+
// unreachable if `selected_version_buf` is `None`
279+
version: self.menu_state.selected_version_buf.clone().unwrap().id,
280+
loader: self.menu_state.selected_loader_buf.clone(),
281+
version_type: self.menu_state.selected_version_type.clone(),
282+
},
283+
});
284+
self.profiles_state.update_config().report_error();
285+
}
286+
}
287+
}
288+
289+
#[test]
290+
fn feature() {
291+
let ver = Loader::Fabric {
292+
version: Some("123".into()),
293+
};
294+
295+
assert!(matches!(ver, Loader::Fabric { .. }))
296+
}

0 commit comments

Comments
 (0)