chore: added enabling disabled bundled mods from browser (#900)
This commit is contained in:
parent
76d929b82a
commit
959458abde
8 changed files with 353 additions and 54 deletions
|
|
@ -98,7 +98,7 @@ pub use package_delete_dialog::use_shared_delete;
|
|||
pub(crate) use package_row::{
|
||||
CARD_BG, CARD_GRID_H, CARD_H, CARD_NAME, GRID_GAP, GRID_MIN_W, badge, disable_warning_body,
|
||||
grid_card, icon_box, kebab_button, meta_size, meta_text, on_secondary, package_icon, pill,
|
||||
remote_icon, toggle_action,
|
||||
remote_icon, set_enabled_action, toggle_action,
|
||||
};
|
||||
pub use package_row::{
|
||||
CardLayout, PackageEntry, PackageRow, github_badge, package_context_menu, provider_badge,
|
||||
|
|
|
|||
|
|
@ -245,22 +245,38 @@ pub(crate) fn toggle_action(
|
|||
cluster_id: i64,
|
||||
enabled: bool,
|
||||
) -> Option<ClusterAction> {
|
||||
if let Some(hash) = &item.hash {
|
||||
set_enabled_action(
|
||||
cluster_id,
|
||||
item.hash.as_deref(),
|
||||
item.bundle_name.as_deref(),
|
||||
&item.package_id,
|
||||
item.manifest_default,
|
||||
enabled,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn set_enabled_action(
|
||||
cluster_id: i64,
|
||||
hash: Option<&str>,
|
||||
bundle_name: Option<&str>,
|
||||
package_id: &str,
|
||||
manifest_default: bool,
|
||||
enabled: bool,
|
||||
) -> Option<ClusterAction> {
|
||||
if let Some(hash) = hash {
|
||||
Some(ClusterAction::SetArtifactEnabled {
|
||||
cluster_id,
|
||||
hash: hash.clone(),
|
||||
hash: hash.to_string(),
|
||||
enabled,
|
||||
})
|
||||
} else {
|
||||
item.bundle_name
|
||||
.clone()
|
||||
.map(|bundle_name| ClusterAction::SetBundlePackageEnabled {
|
||||
cluster_id,
|
||||
bundle_name,
|
||||
package_id: item.package_id.clone(),
|
||||
enabled,
|
||||
manifest_default: item.manifest_default,
|
||||
})
|
||||
bundle_name.map(|bundle_name| ClusterAction::SetBundlePackageEnabled {
|
||||
cluster_id,
|
||||
bundle_name: bundle_name.to_string(),
|
||||
package_id: package_id.to_string(),
|
||||
enabled,
|
||||
manifest_default,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ use oneclient_core::clusters::ModpackSource;
|
|||
|
||||
use crate::components::{Button, Icon, IconType};
|
||||
use crate::hooks::{
|
||||
content_type_for_slug, use_browser_compat, use_cluster, use_dispatch, use_installs_snapshot,
|
||||
use_package_versions_when, version_list,
|
||||
ClusterAction, content_type_for_slug, use_browser_compat, use_cluster, use_dispatch,
|
||||
use_installs_snapshot, use_package_versions_when, version_list,
|
||||
};
|
||||
use crate::routes::Route;
|
||||
use crate::theme::colors;
|
||||
|
|
@ -21,10 +21,25 @@ type InstalledMap = HashMap<(ProviderId, String), Installed>;
|
|||
/// Height of the install control, matched by the installed pill that replaces it
|
||||
const INSTALL_BUTTON_H: f32 = 28.;
|
||||
|
||||
fn installed_for(installed: &InstalledMap, item: &ProjectSummary) -> Option<InstallSource> {
|
||||
#[derive(Clone, PartialEq)]
|
||||
struct CardInstalled {
|
||||
source: InstallSource,
|
||||
enable: Option<ClusterAction>,
|
||||
}
|
||||
|
||||
fn installed_for(
|
||||
installed: &InstalledMap,
|
||||
item: &ProjectSummary,
|
||||
cluster_id: i64,
|
||||
) -> Option<CardInstalled> {
|
||||
installed
|
||||
.get(&(item.provider, item.id.clone()))
|
||||
.map(|installed| installed.source)
|
||||
.map(|installed| CardInstalled {
|
||||
source: installed.source,
|
||||
enable: installed
|
||||
.disabled_bundled()
|
||||
.and_then(|version| version.enable_action(cluster_id)),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn grid_row(
|
||||
|
|
@ -36,9 +51,9 @@ pub(super) fn grid_row(
|
|||
) -> impl IntoElement {
|
||||
let package_type = package_type.to_string();
|
||||
let fill = cols.saturating_sub(row.len());
|
||||
let installed: Vec<Option<InstallSource>> = row
|
||||
let installed: Vec<Option<CardInstalled>> = row
|
||||
.iter()
|
||||
.map(|item| installed_for(installed, item))
|
||||
.map(|item| installed_for(installed, item, cluster_id))
|
||||
.collect();
|
||||
|
||||
rect()
|
||||
|
|
@ -77,7 +92,7 @@ struct PackageCard {
|
|||
item: ProjectSummary,
|
||||
cluster_id: i64,
|
||||
package_type: String,
|
||||
installed: Option<InstallSource>,
|
||||
installed: Option<CardInstalled>,
|
||||
}
|
||||
|
||||
impl PackageCard {
|
||||
|
|
@ -85,7 +100,7 @@ impl PackageCard {
|
|||
item: ProjectSummary,
|
||||
cluster_id: i64,
|
||||
package_type: String,
|
||||
installed: Option<InstallSource>,
|
||||
installed: Option<CardInstalled>,
|
||||
) -> Self {
|
||||
Self {
|
||||
item,
|
||||
|
|
@ -202,7 +217,7 @@ impl Component for PackageCard {
|
|||
&self.item,
|
||||
self.cluster_id,
|
||||
&self.package_type,
|
||||
self.installed,
|
||||
self.installed.clone(),
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
|
@ -215,7 +230,7 @@ pub(super) fn list_row(
|
|||
installed: &InstalledMap,
|
||||
) -> impl IntoElement {
|
||||
ListRow {
|
||||
installed: installed_for(installed, &item),
|
||||
installed: installed_for(installed, &item, cluster_id),
|
||||
item,
|
||||
cluster_id,
|
||||
package_type: package_type.to_string(),
|
||||
|
|
@ -227,7 +242,7 @@ struct ListRow {
|
|||
item: ProjectSummary,
|
||||
cluster_id: i64,
|
||||
package_type: String,
|
||||
installed: Option<InstallSource>,
|
||||
installed: Option<CardInstalled>,
|
||||
}
|
||||
|
||||
impl Component for ListRow {
|
||||
|
|
@ -308,7 +323,7 @@ impl Component for ListRow {
|
|||
&self.item,
|
||||
self.cluster_id,
|
||||
&self.package_type,
|
||||
self.installed,
|
||||
self.installed.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -320,7 +335,7 @@ struct InstallButton {
|
|||
cluster_id: i64,
|
||||
content_type: ContentType,
|
||||
/// Cluster already has this one, so the control becomes a static pill
|
||||
installed: Option<InstallSource>,
|
||||
installed: Option<CardInstalled>,
|
||||
}
|
||||
|
||||
impl InstallButton {
|
||||
|
|
@ -328,7 +343,7 @@ impl InstallButton {
|
|||
item: &ProjectSummary,
|
||||
cluster_id: i64,
|
||||
package_type: &str,
|
||||
installed: Option<InstallSource>,
|
||||
installed: Option<CardInstalled>,
|
||||
) -> Self {
|
||||
Self {
|
||||
provider: item.provider,
|
||||
|
|
@ -376,7 +391,17 @@ impl Component for InstallButton {
|
|||
let (installing, waiting) =
|
||||
use_installs_snapshot().package_busy(is_modpack, cluster_id, provider, &project_id);
|
||||
|
||||
if let Some(installed) = self.installed {
|
||||
if let Some(installed) = &self.installed {
|
||||
if let Some(action) = installed.enable.clone() {
|
||||
return EnableButton {
|
||||
action,
|
||||
variant: EnableVariant::Card {
|
||||
height: INSTALL_BUTTON_H,
|
||||
},
|
||||
}
|
||||
.into_element();
|
||||
}
|
||||
let installed = installed.source;
|
||||
let color = installed.color();
|
||||
return rect()
|
||||
.horizontal()
|
||||
|
|
|
|||
|
|
@ -19,11 +19,12 @@ use crate::components::{
|
|||
};
|
||||
use crate::hooks::use_cluster;
|
||||
use crate::hooks::{
|
||||
BROWSE_PAGE_SIZE, BrowserUiState, bundles_with_status_items, category_list,
|
||||
cluster_content_items, content_type_for_slug, pick_version_metadata, search_items,
|
||||
search_pending, search_total, settled_or_loading, use_browser_compat, use_browser_state_store,
|
||||
use_browser_type, use_bundles_with_status, use_cluster_content, use_clusters, use_debounced,
|
||||
use_package_categories, use_package_search, use_versions, use_view_state, versions_metadata,
|
||||
BROWSE_PAGE_SIZE, BrowserUiState, bundle_overrides_map, bundles_with_status_items,
|
||||
category_list, cluster_content_items, content_type_for_slug, pick_version_metadata,
|
||||
search_items, search_pending, search_total, settled_or_loading, use_browser_compat,
|
||||
use_browser_state_store, use_browser_type, use_bundle_overrides, use_bundles_with_status,
|
||||
use_cluster_content, use_clusters, use_debounced, use_package_categories, use_package_search,
|
||||
use_versions, use_view_state, versions_metadata,
|
||||
};
|
||||
use crate::routes::Route;
|
||||
use crate::theme::colors;
|
||||
|
|
@ -31,8 +32,8 @@ use crate::ui::grid_columns_for_width;
|
|||
use crate::view::app::cluster::supports_datapacks;
|
||||
|
||||
use super::{
|
||||
InstallSource, Installed, PackageBanner, Thumbnail, WorldInstallPrompt, installed_map,
|
||||
preferred_version,
|
||||
EnableButton, EnableVariant, InstallSource, Installed, PackageBanner, Thumbnail,
|
||||
WorldInstallPrompt, installed_map, preferred_version,
|
||||
};
|
||||
use crate::utils::{abbreviate_number, sort_clusters_for_home};
|
||||
|
||||
|
|
@ -250,6 +251,7 @@ impl Component for BrowserBody {
|
|||
let installed = installed_map(
|
||||
cluster_content_items(&use_cluster_content(cluster_id, content_type)),
|
||||
&bundles_with_status_items(&use_bundles_with_status(cluster_id)),
|
||||
&bundle_overrides_map(&use_bundle_overrides(cluster_id)),
|
||||
);
|
||||
let installed = if matches!(content_type, ContentType::DataPack | ContentType::Modpack) {
|
||||
Default::default()
|
||||
|
|
|
|||
|
|
@ -28,9 +28,12 @@ use std::collections::HashMap;
|
|||
use freya::prelude::*;
|
||||
use oneclient_content::packages::ProviderId;
|
||||
use oneclient_core::{BundleFileKind, BundleWithUpdateStatus, LinkedArtifactInfo};
|
||||
use oneclient_db::models::OverrideType;
|
||||
|
||||
use crate::components::{Icon, IconType};
|
||||
use crate::hooks::{loaded_image, use_cached_image};
|
||||
use crate::components::{Button, Icon, IconType, set_enabled_action};
|
||||
use crate::hooks::{
|
||||
ClusterAction, loaded_image, mutation_is_running, use_cached_image, use_cluster_mutation,
|
||||
};
|
||||
use crate::theme::colors;
|
||||
use crate::ui::{ImageFallbackExt, border_all_color};
|
||||
|
||||
|
|
@ -59,15 +62,36 @@ impl InstallSource {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub(crate) struct BundlePin {
|
||||
pub bundle_name: String,
|
||||
pub package_id: String,
|
||||
pub manifest_default: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub(crate) struct InstalledVersion {
|
||||
pub version_id: String,
|
||||
/// The artifact to remove
|
||||
/// `None` when a bundle names this version but nothing is linked
|
||||
pub hash: Option<String>,
|
||||
/// Only meaningful alongside a `hash` a bundle pin with nothing linked is neither on nor off
|
||||
pub enabled: bool,
|
||||
pub source: InstallSource,
|
||||
pub bundle: Option<BundlePin>,
|
||||
}
|
||||
|
||||
impl InstalledVersion {
|
||||
pub fn enable_action(&self, cluster_id: i64) -> Option<ClusterAction> {
|
||||
let pin = self.bundle.as_ref()?;
|
||||
set_enabled_action(
|
||||
cluster_id,
|
||||
self.hash.as_deref(),
|
||||
Some(&pin.bundle_name),
|
||||
&pin.package_id,
|
||||
pin.manifest_default,
|
||||
true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds every version found rather than an arbitrary winner a cluster can end up with more than one
|
||||
|
|
@ -91,12 +115,20 @@ impl Installed {
|
|||
pub fn is_duplicated(&self) -> bool {
|
||||
self.versions.iter().filter(|v| v.hash.is_some()).count() > 1
|
||||
}
|
||||
|
||||
pub fn disabled_bundled(&self) -> Option<&InstalledVersion> {
|
||||
if self.versions.iter().any(|v| v.enabled) {
|
||||
return None;
|
||||
}
|
||||
self.versions.iter().find(|v| v.bundle.is_some())
|
||||
}
|
||||
}
|
||||
|
||||
/// Local files and a bundle's external files are left out they have no project id to match against
|
||||
pub(crate) fn installed_map(
|
||||
content: Vec<LinkedArtifactInfo>,
|
||||
bundles: &[BundleWithUpdateStatus],
|
||||
overrides: &HashMap<(String, String), String>,
|
||||
) -> HashMap<(ProviderId, String), Installed> {
|
||||
let mut map: HashMap<(ProviderId, String), Installed> = HashMap::new();
|
||||
|
||||
|
|
@ -117,6 +149,7 @@ pub(crate) fn installed_map(
|
|||
hash: Some(item.hash),
|
||||
enabled: item.enabled,
|
||||
source: InstallSource::Manual,
|
||||
bundle: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -124,6 +157,7 @@ pub(crate) fn installed_map(
|
|||
// Bundle membership wins a bundle's files would otherwise read as hand-installed
|
||||
// The manifest pin is only a fallback for a missing linked version
|
||||
for bundle in bundles {
|
||||
let bundle_name = &bundle.archive.manifest.name;
|
||||
for (file, _status) in &bundle.files {
|
||||
if let BundleFileKind::Managed {
|
||||
provider,
|
||||
|
|
@ -132,18 +166,34 @@ pub(crate) fn installed_map(
|
|||
..
|
||||
} = &file.kind
|
||||
{
|
||||
let pin = BundlePin {
|
||||
bundle_name: bundle_name.clone(),
|
||||
package_id: file.kind.package_id(),
|
||||
manifest_default: file.enabled,
|
||||
};
|
||||
match map.get_mut(&(*provider, project_id.clone())) {
|
||||
Some(installed) => {
|
||||
installed.source = InstallSource::Bundled;
|
||||
if let Some(version) = installed
|
||||
let only_copy = installed.versions.len() == 1;
|
||||
match installed
|
||||
.versions
|
||||
.iter_mut()
|
||||
.find(|v| &v.version_id == version_id)
|
||||
{
|
||||
version.source = InstallSource::Bundled;
|
||||
Some(version) => {
|
||||
version.source = InstallSource::Bundled;
|
||||
version.bundle.get_or_insert(pin);
|
||||
}
|
||||
None if only_copy => {
|
||||
installed.versions[0].bundle.get_or_insert(pin);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let user_override = overrides
|
||||
.get(&(bundle_name.clone(), pin.package_id.clone()))
|
||||
.and_then(|o| OverrideType::parse(o));
|
||||
map.insert(
|
||||
(*provider, project_id.clone()),
|
||||
Installed {
|
||||
|
|
@ -151,8 +201,9 @@ pub(crate) fn installed_map(
|
|||
versions: vec![InstalledVersion {
|
||||
version_id: version_id.clone(),
|
||||
hash: None,
|
||||
enabled: false,
|
||||
enabled: oneclient_core::effective_enabled(file, user_override),
|
||||
source: InstallSource::Bundled,
|
||||
bundle: Some(pin),
|
||||
}],
|
||||
},
|
||||
);
|
||||
|
|
@ -165,6 +216,68 @@ pub(crate) fn installed_map(
|
|||
map
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub(crate) enum EnableVariant {
|
||||
Sidebar,
|
||||
Card { height: f32 },
|
||||
VersionRow,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub(crate) struct EnableButton {
|
||||
pub action: ClusterAction,
|
||||
pub variant: EnableVariant,
|
||||
}
|
||||
|
||||
impl Component for EnableButton {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
let mutation = use_cluster_mutation();
|
||||
let running = mutation_is_running(&mutation);
|
||||
let action = self.action.clone();
|
||||
|
||||
let button = Button::new()
|
||||
.enabled(!running)
|
||||
.on_press(move |_| mutation.mutate(action.clone()));
|
||||
|
||||
match self.variant {
|
||||
EnableVariant::Sidebar => button
|
||||
.primary()
|
||||
.width(Size::fill())
|
||||
.child(Icon::new(IconType::CheckCircle).size(14.))
|
||||
.text(if running { "Enabling..." } else { "Enable" })
|
||||
.into_element(),
|
||||
EnableVariant::Card { height } => rect()
|
||||
.on_press(|e: Event<PressEventData>| e.stop_propagation())
|
||||
.child(
|
||||
button
|
||||
.primary()
|
||||
.small()
|
||||
.height(Size::px(height))
|
||||
.padding(Gaps::new_symmetric(0., 11.))
|
||||
.child(
|
||||
Icon::new(if running {
|
||||
IconType::Loading02
|
||||
} else {
|
||||
IconType::CheckCircle
|
||||
})
|
||||
.size(12.)
|
||||
.color(colors::fg_primary()),
|
||||
)
|
||||
.child(
|
||||
label()
|
||||
.text(if running { "Enabling" } else { "Enable" })
|
||||
.font_size(11.)
|
||||
.font_weight(FontWeight::SEMI_BOLD)
|
||||
.max_lines(1)
|
||||
.color(colors::fg_primary()),
|
||||
),
|
||||
)
|
||||
.into_element(),
|
||||
EnableVariant::VersionRow => button.secondary().small().text("Enable").into_element(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn installed_badge(installed: InstallSource, font_size: f32) -> impl IntoElement {
|
||||
badge(installed, font_size, installed.color().with_a(38), None)
|
||||
}
|
||||
|
|
@ -450,6 +563,30 @@ mod tests {
|
|||
}]
|
||||
}
|
||||
|
||||
fn installed_map(
|
||||
content: Vec<LinkedArtifactInfo>,
|
||||
bundles: &[BundleWithUpdateStatus],
|
||||
) -> HashMap<(ProviderId, String), Installed> {
|
||||
super::installed_map(content, bundles, &HashMap::new())
|
||||
}
|
||||
|
||||
fn optional(project_id: &str, version_id: &str) -> BundleFile {
|
||||
BundleFile {
|
||||
enabled: false,
|
||||
..managed(project_id, version_id)
|
||||
}
|
||||
}
|
||||
|
||||
fn overridden(
|
||||
project_id: &str,
|
||||
override_type: OverrideType,
|
||||
) -> HashMap<(String, String), String> {
|
||||
HashMap::from([(
|
||||
("performance".to_string(), project_id.to_string()),
|
||||
override_type.as_str().to_string(),
|
||||
)])
|
||||
}
|
||||
|
||||
fn entry(map: &HashMap<(ProviderId, String), Installed>, project: &str) -> Installed {
|
||||
map.get(&(ProviderId::Modrinth, project.to_string()))
|
||||
.expect("project missing from the map")
|
||||
|
|
@ -578,4 +715,96 @@ mod tests {
|
|||
);
|
||||
assert_eq!(sodium.source, InstallSource::Manual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_optional_mod_never_downloaded_offers_enable_through_the_bundle() {
|
||||
let map = installed_map(Vec::new(), &bundles(vec![optional("sodium", "v1")]));
|
||||
|
||||
let sodium = entry(&map, "sodium");
|
||||
let disabled = sodium.disabled_bundled().expect("offers enable");
|
||||
assert!(matches!(
|
||||
disabled.enable_action(7),
|
||||
Some(ClusterAction::SetBundlePackageEnabled {
|
||||
cluster_id: 7,
|
||||
enabled: true,
|
||||
manifest_default: false,
|
||||
..
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_enabled_pin_waiting_for_sync_does_not_offer_enable() {
|
||||
let map = installed_map(Vec::new(), &bundles(vec![managed("sodium", "v1")]));
|
||||
|
||||
assert!(entry(&map, "sodium").disabled_bundled().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_disabled_override_on_an_unlinked_pin_offers_enable() {
|
||||
let map = super::installed_map(
|
||||
Vec::new(),
|
||||
&bundles(vec![managed("sodium", "v1")]),
|
||||
&overridden("sodium", OverrideType::Disabled),
|
||||
);
|
||||
|
||||
let sodium = entry(&map, "sodium");
|
||||
assert!(matches!(
|
||||
sodium.disabled_bundled().and_then(|v| v.enable_action(7)),
|
||||
Some(ClusterAction::SetBundlePackageEnabled {
|
||||
manifest_default: true,
|
||||
..
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_disabled_linked_bundle_artifact_is_enabled_by_hash() {
|
||||
let map = installed_map(
|
||||
vec![disabled_linked("sodium", Some("v1"), "hash-1", false)],
|
||||
&bundles(vec![managed("sodium", "v1")]),
|
||||
);
|
||||
|
||||
let sodium = entry(&map, "sodium");
|
||||
assert!(matches!(
|
||||
sodium.disabled_bundled().and_then(|v| v.enable_action(7)),
|
||||
Some(ClusterAction::SetArtifactEnabled { ref hash, enabled: true, .. }) if hash == "hash-1"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_disabled_copy_from_an_older_bundle_pin_still_offers_enable() {
|
||||
let map = installed_map(
|
||||
vec![disabled_linked("sodium", Some("v1"), "hash-1", false)],
|
||||
&bundles(vec![managed("sodium", "v2")]),
|
||||
);
|
||||
|
||||
assert!(entry(&map, "sodium").disabled_bundled().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn another_enabled_copy_hides_enable() {
|
||||
let map = installed_map(
|
||||
vec![
|
||||
disabled_linked("sodium", Some("v1"), "hash-1", false),
|
||||
linked("sodium", Some("v2"), "hash-2"),
|
||||
],
|
||||
&bundles(vec![managed("sodium", "v1")]),
|
||||
);
|
||||
|
||||
assert!(
|
||||
entry(&map, "sodium").disabled_bundled().is_none(),
|
||||
"enabling the bundle copy would load two"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_hand_installed_disabled_mod_is_not_offered_enable() {
|
||||
let map = installed_map(
|
||||
vec![disabled_linked("sodium", Some("v1"), "hash-1", false)],
|
||||
&[],
|
||||
);
|
||||
|
||||
assert!(entry(&map, "sodium").disabled_bundled().is_none());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,17 +8,18 @@ use oneclient_core::clusters::ModpackSource;
|
|||
use crate::components::{ScrollArea, use_shared_delete};
|
||||
use crate::hooks::use_cluster;
|
||||
use crate::hooks::{
|
||||
bundles_with_status_items, cluster_content_items, content_type_for_slug, package_meta_batch,
|
||||
project_detail, use_browser_compat, use_bundles_with_status, use_cluster_content,
|
||||
use_dispatch, use_installs_snapshot, use_link_confirm, use_package_meta_batch,
|
||||
use_package_project, use_package_versions, version_list, versions_total,
|
||||
bundle_overrides_map, bundles_with_status_items, cluster_content_items, content_type_for_slug,
|
||||
package_meta_batch, project_detail, use_browser_compat, use_bundle_overrides,
|
||||
use_bundles_with_status, use_cluster_content, use_dispatch, use_installs_snapshot,
|
||||
use_link_confirm, use_package_meta_batch, use_package_project, use_package_versions,
|
||||
version_list, versions_total,
|
||||
};
|
||||
use crate::theme::colors;
|
||||
use crate::ui::border_all_color;
|
||||
|
||||
use super::{
|
||||
Installed, InstalledVersion, PackageBanner, Thumbnail, WorldInstallPrompt, activity_badge,
|
||||
installed_badge, installed_map, preferred_version,
|
||||
EnableButton, EnableVariant, Installed, InstalledVersion, PackageBanner, Thumbnail,
|
||||
WorldInstallPrompt, activity_badge, installed_badge, installed_map, preferred_version,
|
||||
};
|
||||
use crate::utils::abbreviate_number;
|
||||
|
||||
|
|
@ -196,6 +197,7 @@ impl Component for BrowserPackage {
|
|||
let installed = installed_map(
|
||||
cluster_content_items(&use_cluster_content(cluster_id, content_type)),
|
||||
&bundles_with_status_items(&use_bundles_with_status(cluster_id)),
|
||||
&bundle_overrides_map(&use_bundle_overrides(cluster_id)),
|
||||
)
|
||||
.remove(&(provider, project_id.clone()))
|
||||
.filter(|_| !is_datapack);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use oneclient_content::packages::types::{
|
|||
use crate::components::{
|
||||
Button, Icon, IconType, Markdown, MarkdownStyle, Segment, SegmentedControl,
|
||||
};
|
||||
use crate::hooks::VERSIONS_PAGE_SIZE;
|
||||
use crate::hooks::{ClusterAction, VERSIONS_PAGE_SIZE};
|
||||
use crate::theme::colors;
|
||||
use crate::ui::border_all_color;
|
||||
use crate::utils::format_size;
|
||||
|
|
@ -142,6 +142,11 @@ pub(super) fn versions_panel(
|
|||
let duplicated = installed
|
||||
.as_ref()
|
||||
.is_some_and(|installed| installed.is_duplicated());
|
||||
let enable = installed
|
||||
.as_ref()
|
||||
.and_then(Installed::disabled_bundled)
|
||||
.filter(|disabled| disabled.version_id == v.version_id)
|
||||
.and_then(|disabled| disabled.enable_action(installer.cluster_id));
|
||||
version_row(
|
||||
v,
|
||||
&dependency_names,
|
||||
|
|
@ -151,6 +156,7 @@ pub(super) fn versions_panel(
|
|||
tag,
|
||||
duplicated,
|
||||
installing,
|
||||
enable,
|
||||
)
|
||||
.into_element()
|
||||
}))
|
||||
|
|
@ -226,6 +232,7 @@ fn version_row(
|
|||
// Saying which version is live only tells the user anything when there are several
|
||||
duplicated: bool,
|
||||
installing: bool,
|
||||
enable: Option<ClusterAction>,
|
||||
) -> impl IntoElement {
|
||||
let version_id = v.version_id.clone();
|
||||
let mut chips: Vec<String> = v.loaders.iter().map(|l| l.to_string()).collect();
|
||||
|
|
@ -300,9 +307,17 @@ fn version_row(
|
|||
.filter(|_| duplicated)
|
||||
.map(|installed| activity_badge(installed.enabled).into_element()),
|
||||
)
|
||||
.child(version_button(
|
||||
installed, v.name, project_id, version_id, installer, on_remove, installing,
|
||||
))
|
||||
.child(match enable {
|
||||
Some(action) => EnableButton {
|
||||
action,
|
||||
variant: EnableVariant::VersionRow,
|
||||
}
|
||||
.into_element(),
|
||||
None => version_button(
|
||||
installed, v.name, project_id, version_id, installer, on_remove, installing,
|
||||
)
|
||||
.into_element(),
|
||||
})
|
||||
}
|
||||
|
||||
/// A bundle pin with nothing linked leaves nothing to press no artifact to remove and installing by hand would duplicate it
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ pub(super) fn sidebar(
|
|||
_ => false,
|
||||
};
|
||||
let can_install = latest_version.is_some() && !have_latest && !installing && !waiting;
|
||||
let enable = installed
|
||||
.as_ref()
|
||||
.and_then(Installed::disabled_bundled)
|
||||
.and_then(|version| version.enable_action(installer.cluster_id));
|
||||
|
||||
rect()
|
||||
.vertical()
|
||||
|
|
@ -101,8 +105,13 @@ pub(super) fn sidebar(
|
|||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
Button::new()
|
||||
.child(match enable {
|
||||
Some(action) => EnableButton {
|
||||
action,
|
||||
variant: EnableVariant::Sidebar,
|
||||
}
|
||||
.into_element(),
|
||||
None => Button::new()
|
||||
.primary()
|
||||
.width(Size::fill())
|
||||
.enabled(can_install)
|
||||
|
|
@ -118,8 +127,9 @@ pub(super) fn sidebar(
|
|||
"Latest installed"
|
||||
} else {
|
||||
"Install latest"
|
||||
}),
|
||||
)
|
||||
})
|
||||
.into_element(),
|
||||
})
|
||||
.maybe(
|
||||
!project.members.is_empty() || !project.author.is_empty(),
|
||||
|el| el.child(authors_card(&project, confirm)),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue