feat(ui): Missing Java Popup
This commit is contained in:
parent
a9481a5789
commit
c881bf858d
10 changed files with 516 additions and 248 deletions
278
packages/oneclient_app/src/components/java_install_manager.rs
Normal file
278
packages/oneclient_app/src/components/java_install_manager.rs
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
use freya::prelude::*;
|
||||
use oneclient_core::java::{AvailableJava, JavaVendor};
|
||||
|
||||
use crate::components::{Button, Dropdown, Icon, IconType, OverlayPopup, ScrollArea};
|
||||
use crate::hooks::{provider_versions, use_provider_versions};
|
||||
use crate::theme::colors;
|
||||
use crate::ui::border_all_color;
|
||||
|
||||
fn providers() -> Vec<(JavaVendor, &'static str)> {
|
||||
vec![
|
||||
(JavaVendor::Zulu, "Azul Zulu"),
|
||||
(JavaVendor::Adoptium, "Eclipse Temurin"),
|
||||
(JavaVendor::Corretto, "Amazon Corretto"),
|
||||
(JavaVendor::Liberica, "BellSoft Liberica"),
|
||||
]
|
||||
}
|
||||
|
||||
/// Reusable "Install Java" picker. Lists downloadable runtimes per provider and
|
||||
/// calls `on_install` with the chosen `(vendor, major)`. When `suggested` is set,
|
||||
/// the matching major is pulled to the top and highlighted.
|
||||
#[derive(PartialEq)]
|
||||
pub struct JavaInstallManager {
|
||||
suggested: Option<u32>,
|
||||
on_install: Option<EventHandler<(JavaVendor, u32)>>,
|
||||
on_close: Option<EventHandler<()>>,
|
||||
}
|
||||
|
||||
impl Default for JavaInstallManager {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl JavaInstallManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
suggested: None,
|
||||
on_install: None,
|
||||
on_close: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn suggested(mut self, major: Option<u32>) -> Self {
|
||||
self.suggested = major;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_install(mut self, handler: impl Into<EventHandler<(JavaVendor, u32)>>) -> Self {
|
||||
self.on_install = Some(handler.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_close(mut self, handler: impl Into<EventHandler<()>>) -> Self {
|
||||
self.on_close = Some(handler.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for JavaInstallManager {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
let providers = providers();
|
||||
let mut selected = use_state(|| 0usize);
|
||||
let suggested = self.suggested;
|
||||
|
||||
let idx = (*selected.read()).min(providers.len() - 1);
|
||||
let (vendor, _) = providers[idx].clone();
|
||||
|
||||
let versions_query = use_provider_versions(vendor.clone());
|
||||
let (mut versions, loading) = provider_versions(&versions_query);
|
||||
|
||||
// Pull the suggested major to the top so it reads as the default choice.
|
||||
if let Some(major) = suggested {
|
||||
versions.sort_by_key(|v| v.major != major);
|
||||
}
|
||||
|
||||
let provider_labels: Vec<String> =
|
||||
providers.iter().map(|(_, name)| name.to_string()).collect();
|
||||
let current_label = provider_labels[idx].clone();
|
||||
|
||||
let inner: Element = if loading {
|
||||
status_text("Loading available versions...")
|
||||
} else if versions.is_empty() {
|
||||
status_text("No versions available from this provider.")
|
||||
} else {
|
||||
let mut area = ScrollArea::new()
|
||||
.width(Size::fill())
|
||||
.height(Size::fill())
|
||||
.spacing(8.);
|
||||
for available in versions {
|
||||
let highlight = suggested == Some(available.major);
|
||||
area = area.child(
|
||||
VersionRow {
|
||||
vendor: vendor.clone(),
|
||||
available,
|
||||
suggested: highlight,
|
||||
on_install: self.on_install.clone(),
|
||||
}
|
||||
.into_element(),
|
||||
);
|
||||
}
|
||||
area.into_element()
|
||||
};
|
||||
|
||||
let list = rect()
|
||||
.width(Size::fill())
|
||||
.height(Size::px(280.))
|
||||
.child(inner)
|
||||
.into_element();
|
||||
|
||||
let close_scrim = self.on_close.clone();
|
||||
let close_button = self.on_close.clone();
|
||||
|
||||
OverlayPopup::new()
|
||||
.on_close(move |()| {
|
||||
if let Some(handler) = &close_scrim {
|
||||
handler.call(());
|
||||
}
|
||||
})
|
||||
.child(
|
||||
rect()
|
||||
.width(Size::window_percent(100.))
|
||||
.height(Size::window_percent(100.))
|
||||
.center()
|
||||
.child(
|
||||
rect()
|
||||
.vertical()
|
||||
.width(Size::px(440.))
|
||||
.max_width(Size::window_percent(90.))
|
||||
.spacing(16.)
|
||||
.padding(Gaps::new_all(20.))
|
||||
.corner_radius(CornerRadius::new_all(16.))
|
||||
.background(colors::page_elevated())
|
||||
.border(border_all_color(1., colors::component_border()))
|
||||
.child(
|
||||
label()
|
||||
.text("Install Java")
|
||||
.font_size(18.)
|
||||
.font_weight(FontWeight::SEMI_BOLD)
|
||||
.color(colors::fg_primary()),
|
||||
)
|
||||
.child(
|
||||
rect()
|
||||
.vertical()
|
||||
.width(Size::fill())
|
||||
.spacing(6.)
|
||||
.child(
|
||||
label()
|
||||
.text("Provider")
|
||||
.font_size(11.)
|
||||
.font_weight(FontWeight::MEDIUM)
|
||||
.color(colors::fg_secondary()),
|
||||
)
|
||||
.child(
|
||||
Dropdown::new(current_label, provider_labels)
|
||||
.width(Size::fill())
|
||||
.height(Size::px(34.))
|
||||
.on_select(move |i: usize| selected.set(i)),
|
||||
),
|
||||
)
|
||||
.child(list)
|
||||
.child(
|
||||
rect()
|
||||
.horizontal()
|
||||
.width(Size::fill())
|
||||
.main_align(Alignment::End)
|
||||
.child(
|
||||
Button::new()
|
||||
.ghost()
|
||||
.on_press(move |_| {
|
||||
if let Some(handler) = &close_button {
|
||||
handler.call(());
|
||||
}
|
||||
})
|
||||
.text("Close"),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.into_element()
|
||||
}
|
||||
}
|
||||
|
||||
fn status_text(text: &'static str) -> Element {
|
||||
rect()
|
||||
.width(Size::fill())
|
||||
.padding(Gaps::new_symmetric(16., 4.))
|
||||
.child(
|
||||
label()
|
||||
.text(text)
|
||||
.font_size(13.)
|
||||
.color(colors::fg_secondary()),
|
||||
)
|
||||
.into_element()
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct VersionRow {
|
||||
vendor: JavaVendor,
|
||||
available: AvailableJava,
|
||||
suggested: bool,
|
||||
on_install: Option<EventHandler<(JavaVendor, u32)>>,
|
||||
}
|
||||
|
||||
impl Component for VersionRow {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
let vendor = self.vendor.clone();
|
||||
let major = self.available.major;
|
||||
let version = self.available.package.name.clone();
|
||||
let on_install = self.on_install.clone();
|
||||
|
||||
let mut title_row = rect()
|
||||
.horizontal()
|
||||
.cross_align(Alignment::Center)
|
||||
.spacing(8.)
|
||||
.child(
|
||||
label()
|
||||
.text(format!("Java {major}"))
|
||||
.font_size(14.)
|
||||
.font_weight(FontWeight::MEDIUM)
|
||||
.color(colors::fg_primary()),
|
||||
);
|
||||
|
||||
if self.suggested {
|
||||
title_row = title_row.child(
|
||||
rect()
|
||||
.padding(Gaps::new_symmetric(2., 8.))
|
||||
.corner_radius(CornerRadius::new_all(6.))
|
||||
.background(colors::brand())
|
||||
.child(
|
||||
label()
|
||||
.text("Suggested")
|
||||
.font_size(10.)
|
||||
.font_weight(FontWeight::SEMI_BOLD)
|
||||
.color(colors::fg_primary()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
rect()
|
||||
.horizontal()
|
||||
.width(Size::fill())
|
||||
.content(Content::Flex)
|
||||
.cross_align(Alignment::Center)
|
||||
.spacing(12.)
|
||||
.padding(Gaps::new_symmetric(10., 12.))
|
||||
.corner_radius(CornerRadius::new_all(10.))
|
||||
.background(colors::component_bg())
|
||||
.maybe(self.suggested, |el| {
|
||||
el.border(border_all_color(1., colors::brand()))
|
||||
})
|
||||
.child(
|
||||
rect()
|
||||
.vertical()
|
||||
.width(Size::flex(1.0))
|
||||
.spacing(2.)
|
||||
.child(title_row)
|
||||
.child(
|
||||
label()
|
||||
.text(version)
|
||||
.font_size(11.)
|
||||
.max_lines(1)
|
||||
.color(colors::fg_secondary()),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
Button::new()
|
||||
.primary()
|
||||
.small()
|
||||
.on_press(move |_| {
|
||||
if let Some(handler) = &on_install {
|
||||
handler.call((vendor.clone(), major));
|
||||
}
|
||||
})
|
||||
.child(Icon::new(IconType::Download01).size(13.))
|
||||
.text("Install"),
|
||||
)
|
||||
}
|
||||
}
|
||||
138
packages/oneclient_app/src/components/java_prompt.rs
Normal file
138
packages/oneclient_app/src/components/java_prompt.rs
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
use freya::prelude::*;
|
||||
use oneclient_core::java::JavaVendor;
|
||||
use oneclient_core::notification::{PromptKind, UserChoice};
|
||||
|
||||
use crate::components::{Button, Icon, IconType, JavaInstallManager, OverlayPopup};
|
||||
use crate::hooks::{use_dispatch, use_notifications_snapshot};
|
||||
use crate::theme::colors;
|
||||
use crate::ui::border_all_color;
|
||||
|
||||
const CARD_BG: Color = Color::from_rgb(26, 34, 41);
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct JavaPromptOverlay;
|
||||
|
||||
impl Component for JavaPromptOverlay {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
// Hooks must run unconditionally, before any early return, so the hook
|
||||
// count stays stable whether or not a Java prompt is pending.
|
||||
let snapshot = use_notifications_snapshot();
|
||||
let dispatch = use_dispatch();
|
||||
let mut show_manager = use_state(|| false);
|
||||
|
||||
let Some(prompt) = snapshot.pending_prompt.clone() else {
|
||||
return rect().into_element();
|
||||
};
|
||||
|
||||
let PromptKind::JavaInstall { major } = prompt.kind else {
|
||||
return rect().into_element();
|
||||
};
|
||||
|
||||
// Download opens the install manager with the required major highlighted.
|
||||
if *show_manager.read() {
|
||||
let install = dispatch.clone();
|
||||
return JavaInstallManager::new()
|
||||
.suggested(Some(major))
|
||||
.on_install(move |(vendor, _row_major): (JavaVendor, u32)| {
|
||||
// The launch needs `major`; the manager only picks the vendor.
|
||||
install.answer_prompt(UserChoice::Install { vendor, major });
|
||||
show_manager.set(false);
|
||||
})
|
||||
.on_close(move |()| show_manager.set(false))
|
||||
.into_element();
|
||||
}
|
||||
|
||||
let close = dispatch.clone();
|
||||
let cancel = dispatch.clone();
|
||||
let folder = dispatch.clone();
|
||||
|
||||
let pick_folder = move |_| {
|
||||
let dispatch = folder.clone();
|
||||
spawn(async move {
|
||||
if let Some(handle) = rfd::AsyncFileDialog::new()
|
||||
.set_title(format!("Select a Java {major} installation folder"))
|
||||
.pick_folder()
|
||||
.await
|
||||
{
|
||||
dispatch.answer_prompt(UserChoice::Folder(handle.path().to_path_buf()));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
OverlayPopup::new()
|
||||
.on_close(move |_| close.answer_prompt(UserChoice::Cancel))
|
||||
.child(
|
||||
rect()
|
||||
.width(Size::window_percent(100.))
|
||||
.height(Size::window_percent(100.))
|
||||
.center()
|
||||
.child(
|
||||
rect()
|
||||
.vertical()
|
||||
.width(Size::px(440.))
|
||||
.max_width(Size::window_percent(90.))
|
||||
.spacing(14.)
|
||||
.padding(Gaps::new_all(20.))
|
||||
.corner_radius(CornerRadius::new_all(14.))
|
||||
.background(CARD_BG)
|
||||
.border(border_all_color(1., colors::component_border()))
|
||||
.child(
|
||||
rect()
|
||||
.horizontal()
|
||||
.cross_align(Alignment::Center)
|
||||
.spacing(10.)
|
||||
.child(
|
||||
Icon::new(IconType::FolderDownload)
|
||||
.size(20.)
|
||||
.color(colors::brand()),
|
||||
)
|
||||
.child(
|
||||
label()
|
||||
.text(prompt.title.clone())
|
||||
.font_size(16.)
|
||||
.font_weight(FontWeight::SEMI_BOLD)
|
||||
.color(colors::fg_primary()),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
label()
|
||||
.text(prompt.question.clone())
|
||||
.font_size(12.)
|
||||
.max_lines(4)
|
||||
.width(Size::fill())
|
||||
.color(colors::fg_secondary()),
|
||||
)
|
||||
.child(
|
||||
rect()
|
||||
.horizontal()
|
||||
.width(Size::fill())
|
||||
.main_align(Alignment::End)
|
||||
.spacing(8.)
|
||||
.child(
|
||||
Button::new()
|
||||
.secondary()
|
||||
.on_press(move |_| {
|
||||
cancel.answer_prompt(UserChoice::Cancel)
|
||||
})
|
||||
.text("Cancel"),
|
||||
)
|
||||
.child(
|
||||
Button::new()
|
||||
.secondary()
|
||||
.on_press(pick_folder)
|
||||
.child(Icon::new(IconType::Folder).size(14.))
|
||||
.text("Choose folder"),
|
||||
)
|
||||
.child(
|
||||
Button::new()
|
||||
.primary()
|
||||
.on_press(move |_| show_manager.set(true))
|
||||
.child(Icon::new(IconType::DownloadCloud02).size(14.))
|
||||
.text("Download"),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.into_element()
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,8 @@ mod context_menu;
|
|||
mod dropdown;
|
||||
mod dynamic_art;
|
||||
mod icons;
|
||||
mod java_install_manager;
|
||||
mod java_prompt;
|
||||
mod link_confirm;
|
||||
mod local_image;
|
||||
mod log_viewer;
|
||||
|
|
@ -42,6 +44,8 @@ pub use context_menu::ContextMenu;
|
|||
pub use dropdown::Dropdown;
|
||||
pub use dynamic_art::DynamicArt;
|
||||
pub use icons::{Icon, IconType};
|
||||
pub use java_install_manager::JavaInstallManager;
|
||||
pub use java_prompt::JavaPromptOverlay;
|
||||
pub use link_confirm::ConfirmLinkOverlay;
|
||||
pub use local_image::LocalImage;
|
||||
pub use log_viewer::LogViewer;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ use freya::prelude::*;
|
|||
use freya::router::*;
|
||||
|
||||
use crate::components::{
|
||||
AccountSwitcher, ClusterUpdatePopup, NotificationCenter, StatusBar, Toasts, UpdatePromptOverlay,
|
||||
AccountSwitcher, ClusterUpdatePopup, JavaPromptOverlay, NotificationCenter, StatusBar, Toasts,
|
||||
UpdatePromptOverlay,
|
||||
};
|
||||
use crate::routes::Route;
|
||||
use crate::theme;
|
||||
|
|
@ -31,6 +32,7 @@ impl Component for RootLayout {
|
|||
.child(AccountSwitcher)
|
||||
.child(Toasts)
|
||||
.child(UpdatePromptOverlay)
|
||||
.child(JavaPromptOverlay)
|
||||
.child(ClusterUpdatePopup)
|
||||
.child(StatusBar)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -312,15 +312,6 @@ impl NotificationState {
|
|||
kind,
|
||||
reply_tx,
|
||||
} => {
|
||||
let entry_id = self.push_inbox(
|
||||
inbox,
|
||||
title.clone(),
|
||||
question.clone(),
|
||||
NotificationLevel::Info,
|
||||
None,
|
||||
false,
|
||||
);
|
||||
self.push_ephemeral_toast(entry_id, MESSAGE_TOAST_TTL);
|
||||
let pending_prompt = Some(PendingPrompt {
|
||||
title: title.clone(),
|
||||
question: question.clone(),
|
||||
|
|
|
|||
|
|
@ -1,33 +1,22 @@
|
|||
use freya::prelude::*;
|
||||
use oneclient_core::java::{AvailableJava, JavaRuntime, JavaVendor};
|
||||
use oneclient_core::java::{JavaRuntime, JavaVendor};
|
||||
|
||||
use super::settings_page;
|
||||
use crate::components::{Button, Dropdown, Icon, IconType, OverlayPopup, ScrollArea};
|
||||
use crate::hooks::{
|
||||
BridgeDispatch, java_runtimes, provider_versions, use_dispatch, use_java_runtimes,
|
||||
use_provider_versions,
|
||||
};
|
||||
use crate::components::{Button, Icon, IconType, JavaInstallManager, ScrollArea};
|
||||
use crate::hooks::{BridgeDispatch, java_runtimes, use_dispatch, use_java_runtimes};
|
||||
use crate::theme::colors;
|
||||
use crate::ui::border_all_color;
|
||||
use crate::view::app::settings::section_header;
|
||||
|
||||
fn providers() -> Vec<(JavaVendor, &'static str)> {
|
||||
vec![
|
||||
(JavaVendor::Zulu, "Azul Zulu"),
|
||||
(JavaVendor::Adoptium, "Eclipse Temurin"),
|
||||
(JavaVendor::Corretto, "Amazon Corretto"),
|
||||
(JavaVendor::Liberica, "BellSoft Liberica"),
|
||||
]
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct SettingsJava;
|
||||
|
||||
impl Component for SettingsJava {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
let dispatch = use_dispatch();
|
||||
let runtimes_query = use_java_runtimes();
|
||||
let runtimes = java_runtimes(&runtimes_query);
|
||||
let show_manager = use_state(|| false);
|
||||
let mut show_manager = use_state(|| false);
|
||||
|
||||
let mut shell = settings_page()
|
||||
.child(section_header("ADD RUNTIME"))
|
||||
|
|
@ -36,7 +25,15 @@ impl Component for SettingsJava {
|
|||
.child(runtimes_table(runtimes));
|
||||
|
||||
if *show_manager.read() {
|
||||
shell = shell.child(InstallManagerPopup { show_manager }.into_element());
|
||||
shell = shell.child(
|
||||
JavaInstallManager::new()
|
||||
.on_install(move |(vendor, major): (JavaVendor, u32)| {
|
||||
dispatch.install_java_runtime(vendor, major);
|
||||
show_manager.set(false);
|
||||
})
|
||||
.on_close(move |()| show_manager.set(false))
|
||||
.into_element(),
|
||||
);
|
||||
}
|
||||
|
||||
shell.into_element()
|
||||
|
|
@ -249,185 +246,3 @@ fn remove_button(dispatch: BridgeDispatch, path: String) -> impl IntoElement {
|
|||
)
|
||||
.into_element()
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct InstallManagerPopup {
|
||||
show_manager: State<bool>,
|
||||
}
|
||||
|
||||
impl Component for InstallManagerPopup {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
let mut show_manager = self.show_manager;
|
||||
let providers = providers();
|
||||
let mut selected = use_state(|| 0usize);
|
||||
|
||||
let idx = (*selected.read()).min(providers.len() - 1);
|
||||
let (vendor, _) = providers[idx].clone();
|
||||
|
||||
let versions_query = use_provider_versions(vendor.clone());
|
||||
let (versions, loading) = provider_versions(&versions_query);
|
||||
|
||||
let provider_labels: Vec<String> =
|
||||
providers.iter().map(|(_, name)| name.to_string()).collect();
|
||||
let current_label = provider_labels[idx].clone();
|
||||
|
||||
let inner: Element = if loading {
|
||||
status_text("Loading available versions...")
|
||||
} else if versions.is_empty() {
|
||||
status_text("No versions available from this provider.")
|
||||
} else {
|
||||
let mut area = ScrollArea::new()
|
||||
.width(Size::fill())
|
||||
.height(Size::fill())
|
||||
.spacing(8.);
|
||||
for available in versions {
|
||||
area = area.child(
|
||||
VersionRow {
|
||||
vendor: vendor.clone(),
|
||||
available,
|
||||
show_manager,
|
||||
}
|
||||
.into_element(),
|
||||
);
|
||||
}
|
||||
area.into_element()
|
||||
};
|
||||
|
||||
let list = rect()
|
||||
.width(Size::fill())
|
||||
.height(Size::px(280.))
|
||||
.child(inner)
|
||||
.into_element();
|
||||
|
||||
OverlayPopup::new()
|
||||
.on_close(move |()| show_manager.set(false))
|
||||
.child(
|
||||
rect()
|
||||
.width(Size::window_percent(100.))
|
||||
.height(Size::window_percent(100.))
|
||||
.center()
|
||||
.child(
|
||||
rect()
|
||||
.vertical()
|
||||
.width(Size::px(440.))
|
||||
.max_width(Size::window_percent(90.))
|
||||
.spacing(16.)
|
||||
.padding(Gaps::new_all(20.))
|
||||
.corner_radius(CornerRadius::new_all(16.))
|
||||
.background(colors::page_elevated())
|
||||
.border(border_all_color(1., colors::component_border()))
|
||||
.child(
|
||||
label()
|
||||
.text("Install Java")
|
||||
.font_size(18.)
|
||||
.font_weight(FontWeight::SEMI_BOLD)
|
||||
.color(colors::fg_primary()),
|
||||
)
|
||||
.child(
|
||||
rect()
|
||||
.vertical()
|
||||
.width(Size::fill())
|
||||
.spacing(6.)
|
||||
.child(
|
||||
label()
|
||||
.text("Provider")
|
||||
.font_size(11.)
|
||||
.font_weight(FontWeight::MEDIUM)
|
||||
.color(colors::fg_secondary()),
|
||||
)
|
||||
.child(
|
||||
Dropdown::new(current_label, provider_labels)
|
||||
.width(Size::fill())
|
||||
.height(Size::px(34.))
|
||||
.on_select(move |i: usize| selected.set(i)),
|
||||
),
|
||||
)
|
||||
.child(list)
|
||||
.child(
|
||||
rect()
|
||||
.horizontal()
|
||||
.width(Size::fill())
|
||||
.main_align(Alignment::End)
|
||||
.child(
|
||||
Button::new()
|
||||
.ghost()
|
||||
.on_press(move |_| show_manager.set(false))
|
||||
.text("Close"),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.into_element()
|
||||
}
|
||||
}
|
||||
|
||||
fn status_text(text: &'static str) -> Element {
|
||||
rect()
|
||||
.width(Size::fill())
|
||||
.padding(Gaps::new_symmetric(16., 4.))
|
||||
.child(
|
||||
label()
|
||||
.text(text)
|
||||
.font_size(13.)
|
||||
.color(colors::fg_secondary()),
|
||||
)
|
||||
.into_element()
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct VersionRow {
|
||||
vendor: JavaVendor,
|
||||
available: AvailableJava,
|
||||
show_manager: State<bool>,
|
||||
}
|
||||
|
||||
impl Component for VersionRow {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
let dispatch = use_dispatch();
|
||||
let mut show_manager = self.show_manager;
|
||||
let vendor = self.vendor.clone();
|
||||
let major = self.available.major;
|
||||
let version = self.available.package.name.clone();
|
||||
|
||||
rect()
|
||||
.horizontal()
|
||||
.width(Size::fill())
|
||||
.content(Content::Flex)
|
||||
.cross_align(Alignment::Center)
|
||||
.spacing(12.)
|
||||
.padding(Gaps::new_symmetric(10., 12.))
|
||||
.corner_radius(CornerRadius::new_all(10.))
|
||||
.background(colors::component_bg())
|
||||
.child(
|
||||
rect()
|
||||
.vertical()
|
||||
.width(Size::flex(1.0))
|
||||
.spacing(2.)
|
||||
.child(
|
||||
label()
|
||||
.text(format!("Java {major}"))
|
||||
.font_size(14.)
|
||||
.font_weight(FontWeight::MEDIUM)
|
||||
.color(colors::fg_primary()),
|
||||
)
|
||||
.child(
|
||||
label()
|
||||
.text(version)
|
||||
.font_size(11.)
|
||||
.max_lines(1)
|
||||
.color(colors::fg_secondary()),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
Button::new()
|
||||
.primary()
|
||||
.small()
|
||||
.on_press(move |_| {
|
||||
dispatch.install_java_runtime(vendor.clone(), major);
|
||||
show_manager.set(false);
|
||||
})
|
||||
.child(Icon::new(IconType::Download01).size(13.))
|
||||
.text("Install"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ pub async fn launch_cluster(
|
|||
.as_ref()
|
||||
.map(|v| v.major_version)
|
||||
.ok_or(GameError::MissingJavaVersion)?;
|
||||
|
||||
JavaManager::prepare_java(state, major, search_for_java).await?
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@ impl JavaManager {
|
|||
match services.notifier.prompt_java_install(major).await? {
|
||||
UserChoice::Accept => download_and_register(major, services).await,
|
||||
UserChoice::Folder(folder) => register_custom_java(&services.db, folder, major).await,
|
||||
UserChoice::Install { vendor, .. } => {
|
||||
Self::install_runtime_from(services, &vendor, major).await
|
||||
}
|
||||
UserChoice::Cancel => Err(JavaError::Cancelled.into()),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,6 +179,10 @@ pub enum UserChoice {
|
|||
Accept,
|
||||
Cancel,
|
||||
Folder(PathBuf),
|
||||
Install {
|
||||
vendor: crate::java::JavaVendor,
|
||||
major: u32,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
|
|
|
|||
|
|
@ -1,15 +1,33 @@
|
|||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::data::{GroupedProgressEvent, TaskCategory};
|
||||
use super::NotificationService;
|
||||
use super::data::{GroupedProgressEvent, TaskCategory};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SessionInner {
|
||||
session_id: Uuid,
|
||||
notifier: NotificationService,
|
||||
ended: AtomicBool,
|
||||
}
|
||||
|
||||
impl SessionInner {
|
||||
fn end(&self) {
|
||||
if self.ended.swap(true, Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.notifier.send_grouped(GroupedProgressEvent::End {
|
||||
session_id: self.session_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SessionInner {
|
||||
fn drop(&mut self) {
|
||||
self.end();
|
||||
}
|
||||
}
|
||||
|
||||
struct ChildInner {
|
||||
|
|
@ -42,6 +60,7 @@ impl GroupedProgressSession {
|
|||
inner: Arc::new(SessionInner {
|
||||
session_id,
|
||||
notifier: notifier.clone(),
|
||||
ended: AtomicBool::new(false),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
@ -56,12 +75,14 @@ impl GroupedProgressSession {
|
|||
if count == 0 {
|
||||
return;
|
||||
}
|
||||
self.inner.notifier.send_grouped(GroupedProgressEvent::Expect {
|
||||
session_id: self.inner.session_id,
|
||||
category,
|
||||
count,
|
||||
total,
|
||||
});
|
||||
self.inner
|
||||
.notifier
|
||||
.send_grouped(GroupedProgressEvent::Expect {
|
||||
session_id: self.inner.session_id,
|
||||
category,
|
||||
count,
|
||||
total,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn child(
|
||||
|
|
@ -74,13 +95,15 @@ impl GroupedProgressSession {
|
|||
let label = label.into();
|
||||
let total = total.max(1);
|
||||
|
||||
self.inner.notifier.send_grouped(GroupedProgressEvent::AddChild {
|
||||
session_id: self.inner.session_id,
|
||||
child_id,
|
||||
label: label.clone(),
|
||||
total,
|
||||
category,
|
||||
});
|
||||
self.inner
|
||||
.notifier
|
||||
.send_grouped(GroupedProgressEvent::AddChild {
|
||||
session_id: self.inner.session_id,
|
||||
child_id,
|
||||
label: label.clone(),
|
||||
total,
|
||||
category,
|
||||
});
|
||||
|
||||
GroupedProgressChild {
|
||||
inner: Arc::new(ChildInner {
|
||||
|
|
@ -114,11 +137,7 @@ impl GroupedProgressSession {
|
|||
}
|
||||
|
||||
pub fn finish(self) {
|
||||
self.inner
|
||||
.notifier
|
||||
.send_grouped(GroupedProgressEvent::End {
|
||||
session_id: self.inner.session_id,
|
||||
});
|
||||
self.inner.end();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -128,18 +147,28 @@ impl GroupedProgressChild {
|
|||
}
|
||||
|
||||
pub fn set_phase(&self, phase: super::data::TaskPhase) {
|
||||
if self.inner.finished.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
if self
|
||||
.inner
|
||||
.finished
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
self.inner.notifier.send_grouped(GroupedProgressEvent::SetChildPhase {
|
||||
session_id: self.inner.session_id,
|
||||
child_id: self.inner.child_id,
|
||||
phase,
|
||||
});
|
||||
self.inner
|
||||
.notifier
|
||||
.send_grouped(GroupedProgressEvent::SetChildPhase {
|
||||
session_id: self.inner.session_id,
|
||||
child_id: self.inner.child_id,
|
||||
phase,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_progress(&self, current: u64, total: Option<u64>) {
|
||||
if self.inner.finished.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
if self
|
||||
.inner
|
||||
.finished
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -152,12 +181,14 @@ impl GroupedProgressChild {
|
|||
if total > stored {
|
||||
self.inner.total.store(total, Ordering::Relaxed);
|
||||
}
|
||||
self.inner.notifier.send_grouped(GroupedProgressEvent::UpdateChild {
|
||||
session_id: self.inner.session_id,
|
||||
child_id: self.inner.child_id,
|
||||
current,
|
||||
total,
|
||||
});
|
||||
self.inner
|
||||
.notifier
|
||||
.send_grouped(GroupedProgressEvent::UpdateChild {
|
||||
session_id: self.inner.session_id,
|
||||
child_id: self.inner.child_id,
|
||||
current,
|
||||
total,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn finish(&self) {
|
||||
|
|
@ -169,10 +200,12 @@ impl GroupedProgressChild {
|
|||
return;
|
||||
}
|
||||
|
||||
self.inner.notifier.send_grouped(GroupedProgressEvent::FinishChild {
|
||||
session_id: self.inner.session_id,
|
||||
child_id: self.inner.child_id,
|
||||
});
|
||||
self.inner
|
||||
.notifier
|
||||
.send_grouped(GroupedProgressEvent::FinishChild {
|
||||
session_id: self.inner.session_id,
|
||||
child_id: self.inner.child_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -181,4 +214,3 @@ impl Drop for GroupedProgressChild {
|
|||
self.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue