OneLauncher-Cracked/packages/oneclient_app/src/view/app/browser/package/mod.rs
2026-07-28 12:11:02 +02:00

207 lines
6.4 KiB
Rust

use freya::prelude::*;
use oneclient_content::packages::{ContentType, ProviderId};
use crate::components::ScrollArea;
use crate::hooks::{
content_type_for_slug, project_detail, use_browser_compat, use_dispatch, use_link_confirm,
use_package_project, use_package_versions, version_list, versions_total,
};
use crate::theme::colors;
use crate::ui::border_all_color;
use crate::hooks::use_cluster;
use super::{PackageBanner, Thumbnail};
use crate::utils::abbreviate_number;
mod panels;
mod sidebar;
use panels::{about_panel, gallery_panel, loading_body, tabs, versions_panel};
use sidebar::sidebar;
const PANEL_BG: Color = Color::from_rgb(21, 28, 34);
const SIDEBAR_W: f32 = 280.;
const SCROLLBAR_GUTTER: f32 = 18.;
fn decode_package_id(package_id: &str) -> (ProviderId, String) {
match package_id.split_once(':') {
Some((repr, id)) => {
let provider = repr
.parse::<u8>()
.ok()
.and_then(ProviderId::from_repr)
.unwrap_or(ProviderId::Modrinth);
(provider, id.to_string())
}
None => (ProviderId::Modrinth, package_id.to_string()),
}
}
fn pill(text: String) -> impl IntoElement {
rect()
.center()
.padding(Gaps::new_symmetric(2., 8.))
.corner_radius(CornerRadius::new_all(999.))
.background(colors::component_bg())
.border(border_all_color(1., colors::component_border()))
.child(
label()
.text(text)
.font_size(10.)
.max_lines(1)
.color(colors::fg_secondary()),
)
.into_element()
}
fn pill_flow(items: &[String], per_row: usize, max: usize) -> impl IntoElement {
let shown: Vec<String> = items.iter().take(max).cloned().collect();
let overflow = items.len().saturating_sub(shown.len());
let mut all = shown;
if overflow > 0 {
all.push(format!("+{overflow}"));
}
rect()
.vertical()
.spacing(4.)
.children(all.chunks(per_row.max(1)).map(|chunk| {
rect()
.horizontal()
.spacing(4.)
.children(chunk.iter().map(|t| pill(t.clone()).into_element()))
.into_element()
}))
.into_element()
}
fn pill_detail(key: &str, items: &[String]) -> Element {
rect()
.vertical()
.width(Size::fill())
.spacing(6.)
.child(
label()
.text(key.to_string())
.font_size(12.)
.color(colors::fg_secondary()),
)
.child(pill_flow(items, 4, 18))
.into_element()
}
#[derive(PartialEq)]
pub struct BrowserPackage {
pub cluster_id: i64,
pub package_type: String,
pub package_id: String,
}
impl Component for BrowserPackage {
fn render(&self) -> impl IntoElement {
let cluster_id = self.cluster_id;
let (provider, project_id) = decode_package_id(&self.package_id);
let content_type = content_type_for_slug(&self.package_type);
let active_tab = use_state(|| 0usize);
let versions_page = use_state(|| 0usize);
let compatible_only = use_browser_compat();
let dispatch = use_dispatch();
let confirm = use_link_confirm();
let cluster = use_cluster(cluster_id);
let compat = *compatible_only.read();
let (game_version, loader) = match (compat, &cluster) {
(true, Some(c)) => (
Some(c.mc_version.clone()),
(content_type == ContentType::Mod).then_some(c.mc_loader),
),
_ => (None, None),
};
let mut versions_page_state = versions_page;
let mut last_compat = use_state(|| compat);
if *last_compat.peek() != compat {
last_compat.set(compat);
if *versions_page_state.peek() != 0 {
versions_page_state.set(0);
}
}
let project_query = use_package_project(provider, project_id.clone());
let versions_query = use_package_versions(
provider,
project_id.clone(),
game_version,
loader,
*versions_page.read(),
);
let project = project_detail(&project_query);
let versions = version_list(&versions_query);
let total_versions = versions_total(&versions_query);
let latest_version = versions.first().map(|v| v.version_id.clone());
let gallery = project
.as_ref()
.map(|p| p.gallery.clone())
.unwrap_or_default();
let has_gallery = !gallery.is_empty();
let max_tab = if has_gallery { 2 } else { 1 };
let current = (*active_tab.read()).min(max_tab);
let body = match (&project, current) {
(None, _) => loading_body().into_element(),
(Some(project), 0) => about_panel(project).into_element(),
(Some(project), 1) => versions_panel(
versions,
total_versions,
versions_page,
provider,
project.id.clone(),
cluster_id,
dispatch.clone(),
)
.into_element(),
(Some(_), _) => gallery_panel(gallery).into_element(),
};
let row = rect()
.horizontal()
.width(Size::fill())
.cross_align(Alignment::Start)
.spacing(24.)
.content(Content::Flex)
.child(sidebar(
project,
latest_version,
provider,
cluster_id,
dispatch,
confirm,
))
.child(
rect()
.vertical()
.width(Size::flex(1.0))
.spacing(12.)
.child(tabs(active_tab, has_gallery))
.child(body),
)
.into_element();
rect()
.width(Size::fill())
.height(Size::fill())
.overflow(Overflow::Clip)
.padding(Gaps::new(0., 40., 40., 40.))
.child(
ScrollArea::new()
.width(Size::fill())
.height(Size::fill())
.reset_key(current as u64)
.padding(Gaps::new(0., SCROLLBAR_GUTTER, 0., 0.))
.children([row]),
)
.into_element()
}
}