chore(lint): cargo fmt
This commit is contained in:
parent
e0947848f9
commit
daaa209b30
273 changed files with 13173 additions and 12596 deletions
|
|
@ -47,8 +47,14 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn both_spellings_carry_the_folder() {
|
fn both_spellings_carry_the_folder() {
|
||||||
assert_eq!(parse(&["--launch", "fabric-1-20"]).launch.as_deref(), Some("fabric-1-20"));
|
assert_eq!(
|
||||||
assert_eq!(parse(&["--launch=fabric-1-20"]).launch.as_deref(), Some("fabric-1-20"));
|
parse(&["--launch", "fabric-1-20"]).launch.as_deref(),
|
||||||
|
Some("fabric-1-20")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse(&["--launch=fabric-1-20"]).launch.as_deref(),
|
||||||
|
Some("fabric-1-20")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -68,7 +74,12 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unknown_flags_are_ignored_not_fatal() {
|
fn unknown_flags_are_ignored_not_fatal() {
|
||||||
assert_eq!(parse(&["--verbose", "--launch", "x", "leftover"]).launch.as_deref(), Some("x"));
|
assert_eq!(
|
||||||
|
parse(&["--verbose", "--launch", "x", "leftover"])
|
||||||
|
.launch
|
||||||
|
.as_deref(),
|
||||||
|
Some("x")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ use freya::{
|
||||||
use oneclient_auth::MinecraftAccount;
|
use oneclient_auth::MinecraftAccount;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ui::{divider},
|
|
||||||
Route,
|
Route,
|
||||||
components::{Avatar, Button, Icon, IconType, OverlayPopup},
|
components::{Avatar, Button, Icon, IconType, OverlayPopup},
|
||||||
hooks::{
|
hooks::{
|
||||||
|
|
@ -14,6 +13,7 @@ use crate::{
|
||||||
use_accounts, use_current_account, use_dispatch, use_set_default_account,
|
use_accounts, use_current_account, use_dispatch, use_set_default_account,
|
||||||
},
|
},
|
||||||
theme::colors,
|
theme::colors,
|
||||||
|
ui::divider,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@ use freya::router::RouterContext;
|
||||||
use crate::components::{Button, Icon, IconType};
|
use crate::components::{Button, Icon, IconType};
|
||||||
use oneclient_common::parse_mc_version;
|
use oneclient_common::parse_mc_version;
|
||||||
|
|
||||||
use crate::hooks::{settled_or_loading, use_active_cluster_id, use_clusters, use_dispatch, use_game_snapshot, use_launcher, use_version_metadata};
|
use crate::hooks::{
|
||||||
|
settled_or_loading, use_active_cluster_id, use_clusters, use_dispatch, use_game_snapshot,
|
||||||
|
use_launcher, use_version_metadata,
|
||||||
|
};
|
||||||
use crate::routes::Route;
|
use crate::routes::Route;
|
||||||
use crate::theme::colors;
|
use crate::theme::colors;
|
||||||
use crate::utils::sort_clusters_for_home;
|
use crate::utils::sort_clusters_for_home;
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ pub struct Button {
|
||||||
tooltip: Option<Box<str>>,
|
tooltip: Option<Box<str>>,
|
||||||
tooltip_placement: TooltipPlacement,
|
tooltip_placement: TooltipPlacement,
|
||||||
on_press: Option<EventHandler<Event<PressEventData>>>,
|
on_press: Option<EventHandler<Event<PressEventData>>>,
|
||||||
overflow: Overflow,
|
overflow: Overflow,
|
||||||
|
|
||||||
elements: Vec<Element>,
|
elements: Vec<Element>,
|
||||||
key: DiffKey,
|
key: DiffKey,
|
||||||
|
|
@ -118,7 +118,7 @@ impl Button {
|
||||||
tooltip_placement: TooltipPlacement::default(),
|
tooltip_placement: TooltipPlacement::default(),
|
||||||
on_press: None,
|
on_press: None,
|
||||||
elements: Vec::new(),
|
elements: Vec::new(),
|
||||||
overflow: Overflow::Clip,
|
overflow: Overflow::Clip,
|
||||||
key: DiffKey::None,
|
key: DiffKey::None,
|
||||||
cursor_icon: CursorIcon::Pointer,
|
cursor_icon: CursorIcon::Pointer,
|
||||||
}
|
}
|
||||||
|
|
@ -158,10 +158,10 @@ impl Button {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn overflow(mut self, overflow: Overflow) -> Self {
|
pub fn overflow(mut self, overflow: Overflow) -> Self {
|
||||||
self.overflow = overflow;
|
self.overflow = overflow;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn font_size(mut self, font_size: impl Into<f32>) -> Self {
|
pub fn font_size(mut self, font_size: impl Into<f32>) -> Self {
|
||||||
self.text_style.font_size = Some(font_size.into().into());
|
self.text_style.font_size = Some(font_size.into().into());
|
||||||
|
|
|
||||||
|
|
@ -368,7 +368,7 @@ impl Component for BarChart {
|
||||||
rect()
|
rect()
|
||||||
.width(Size::fill())
|
.width(Size::fill())
|
||||||
.height(Size::px(h))
|
.height(Size::px(h))
|
||||||
.corner_radius(CornerRadius::new(4., 4., 0., 0.))
|
.corner_radius(CornerRadius::new(4., 4., 0., 0.))
|
||||||
.background(color),
|
.background(color),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ use freya::{prelude::*, router::RouterContext};
|
||||||
use oneclient_content::packages::{CachedPackageMeta, ProviderId};
|
use oneclient_content::packages::{CachedPackageMeta, ProviderId};
|
||||||
|
|
||||||
use crate::components::{Button, Icon, IconType, OverlayPopup, ScrollArea, TabBar, TabItem};
|
use crate::components::{Button, Icon, IconType, OverlayPopup, ScrollArea, TabBar, TabItem};
|
||||||
use crate::hooks::{package_meta_batch, use_dispatch, use_notifications_snapshot, use_package_meta_batch};
|
use crate::hooks::{
|
||||||
|
package_meta_batch, use_dispatch, use_notifications_snapshot, use_package_meta_batch,
|
||||||
|
};
|
||||||
use crate::notifications::{ClusterUpdateItem, ClusterUpdateSummary};
|
use crate::notifications::{ClusterUpdateItem, ClusterUpdateSummary};
|
||||||
use crate::routes::Route;
|
use crate::routes::Route;
|
||||||
use crate::theme::colors;
|
use crate::theme::colors;
|
||||||
|
|
@ -355,11 +357,7 @@ fn change_list(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Multi-cluster stand-in for the footer's "Open cluster" shortcut
|
/// Multi-cluster stand-in for the footer's "Open cluster" shortcut
|
||||||
fn cluster_header(
|
fn cluster_header(group: &ClusterGroup, first: bool, dispatch: crate::Actions) -> impl IntoElement {
|
||||||
group: &ClusterGroup,
|
|
||||||
first: bool,
|
|
||||||
dispatch: crate::Actions,
|
|
||||||
) -> impl IntoElement {
|
|
||||||
let cluster_id = group.cluster_id;
|
let cluster_id = group.cluster_id;
|
||||||
let count = group.names.len();
|
let count = group.names.len();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,12 @@ impl Component for AccountHeader {
|
||||||
let unusable = query_error(¤t).is_some();
|
let unusable = query_error(¤t).is_some();
|
||||||
let account = try_default_account(¤t).or_else(|| {
|
let account = try_default_account(¤t).or_else(|| {
|
||||||
unusable
|
unusable
|
||||||
.then(|| try_accounts(&accounts_query).unwrap_or_default().into_iter().next())
|
.then(|| {
|
||||||
|
try_accounts(&accounts_query)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
})
|
||||||
.flatten()
|
.flatten()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -164,11 +169,7 @@ impl Component for AccountHeader {
|
||||||
.cross_align(Alignment::Center)
|
.cross_align(Alignment::Center)
|
||||||
.spacing(10.)
|
.spacing(10.)
|
||||||
.padding(Gaps::new_all(4.))
|
.padding(Gaps::new_all(4.))
|
||||||
.child(
|
.child(Avatar::new(uuid).width(Size::px(40.)).height(Size::px(40.)))
|
||||||
Avatar::new(uuid)
|
|
||||||
.width(Size::px(40.))
|
|
||||||
.height(Size::px(40.)),
|
|
||||||
)
|
|
||||||
.child(
|
.child(
|
||||||
rect()
|
rect()
|
||||||
.vertical()
|
.vertical()
|
||||||
|
|
@ -257,11 +258,7 @@ impl Component for NavRow {
|
||||||
.on_pointer_enter(move |_| hovered.set(true))
|
.on_pointer_enter(move |_| hovered.set(true))
|
||||||
.on_pointer_leave(move |_| hovered.set(false))
|
.on_pointer_leave(move |_| hovered.set(false))
|
||||||
.on_press(go)
|
.on_press(go)
|
||||||
.child(
|
.child(Icon::new(self.icon).size(20.).color(colors::fg_secondary()))
|
||||||
Icon::new(self.icon)
|
|
||||||
.size(20.)
|
|
||||||
.color(colors::fg_secondary()),
|
|
||||||
)
|
|
||||||
.child(
|
.child(
|
||||||
label()
|
label()
|
||||||
.text(self.row_label)
|
.text(self.row_label)
|
||||||
|
|
@ -619,9 +616,7 @@ impl Component for FooterAction {
|
||||||
.a11y_role(AccessibilityRole::Button)
|
.a11y_role(AccessibilityRole::Button)
|
||||||
.on_pointer_enter(move |_| hovered.set(true))
|
.on_pointer_enter(move |_| hovered.set(true))
|
||||||
.on_pointer_leave(move |_| hovered.set(false))
|
.on_pointer_leave(move |_| hovered.set(false))
|
||||||
.map(handler, |el, handler| {
|
.map(handler, |el, handler| el.on_press(move |e| handler.call(e)))
|
||||||
el.on_press(move |e| handler.call(e))
|
|
||||||
})
|
|
||||||
.child(Icon::new(self.icon).size(16.).color(self.color))
|
.child(Icon::new(self.icon).size(16.).color(self.color))
|
||||||
.child(
|
.child(
|
||||||
label()
|
label()
|
||||||
|
|
|
||||||
|
|
@ -270,8 +270,7 @@ fn month_grid(
|
||||||
outside: day.month0() != shown.month0(),
|
outside: day.month0() != shown.month0(),
|
||||||
enabled: (min..=max).contains(&day),
|
enabled: (min..=max).contains(&day),
|
||||||
in_range: selected.is_some_and(|(a, b)| (a..=b).contains(&day)),
|
in_range: selected.is_some_and(|(a, b)| (a..=b).contains(&day)),
|
||||||
edge: selected.is_some_and(|(a, b)| day == a || day == b)
|
edge: selected.is_some_and(|(a, b)| day == a || day == b) || pending == Some(day),
|
||||||
|| pending == Some(day),
|
|
||||||
pick: pick.clone(),
|
pick: pick.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -349,7 +348,11 @@ impl<F: FnMut(NaiveDate) + Clone + 'static> Component for DayCell<F> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn footer(has_range: bool, pending: Option<NaiveDate>, mut clear: impl FnMut() + 'static) -> Element {
|
fn footer(
|
||||||
|
has_range: bool,
|
||||||
|
pending: Option<NaiveDate>,
|
||||||
|
mut clear: impl FnMut() + 'static,
|
||||||
|
) -> Element {
|
||||||
let hint = match pending {
|
let hint = match pending {
|
||||||
Some(day) => format!("From {} — pick an end date", format_day(day)),
|
Some(day) => format!("From {} — pick an end date", format_day(day)),
|
||||||
None => "Click a start date, then an end date".to_string(),
|
None => "Click a start date, then an end date".to_string(),
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use freya::prelude::*;
|
use freya::prelude::*;
|
||||||
use oneclient_core::clusters::Cluster;
|
|
||||||
use oneclient_core::images::DEFAULT_IMAGE_EDGE;
|
|
||||||
use oneclient_common::domain::GameLoader;
|
use oneclient_common::domain::GameLoader;
|
||||||
use oneclient_common::{VersionKey, parse_mc_version};
|
use oneclient_common::{VersionKey, parse_mc_version};
|
||||||
|
use oneclient_core::clusters::Cluster;
|
||||||
|
use oneclient_core::images::DEFAULT_IMAGE_EDGE;
|
||||||
|
|
||||||
use crate::AppAssets;
|
use crate::AppAssets;
|
||||||
use crate::hooks::{loaded_image, use_cached_image, use_version_metadata};
|
use crate::hooks::{loaded_image, use_cached_image, use_version_metadata};
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ use std::path::{Path, PathBuf};
|
||||||
use freya::animation::{AnimNum, Ease, Function, OnCreation, use_animation};
|
use freya::animation::{AnimNum, Ease, Function, OnCreation, use_animation};
|
||||||
use freya::prelude::*;
|
use freya::prelude::*;
|
||||||
use freya::router::use_route;
|
use freya::router::use_route;
|
||||||
use oneclient_core::clusters::Cluster;
|
|
||||||
use oneclient_content::packages::ContentType;
|
use oneclient_content::packages::ContentType;
|
||||||
|
use oneclient_core::clusters::Cluster;
|
||||||
use oneclient_db::models::ClusterId;
|
use oneclient_db::models::ClusterId;
|
||||||
|
|
||||||
use crate::Route;
|
use crate::Route;
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,7 @@ impl Component for GenericPromptOverlay {
|
||||||
.pick_folder()
|
.pick_folder()
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
dispatch.answer_prompt(
|
dispatch.answer_prompt(Answer::new(id).with_folder(handle.path()));
|
||||||
Answer::new(id).with_folder(handle.path()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use freya::prelude::*;
|
use freya::prelude::*;
|
||||||
use oneclient_java::{JAVA_CHOICE_DOWNLOAD, JAVA_CHOICE_FOLDER, JavaVendor};
|
|
||||||
use oneclient_events::Answer;
|
use oneclient_events::Answer;
|
||||||
|
use oneclient_java::{JAVA_CHOICE_DOWNLOAD, JAVA_CHOICE_FOLDER, JavaVendor};
|
||||||
|
|
||||||
use crate::components::{Button, Icon, IconType, JavaInstallManager, OverlayPopup};
|
use crate::components::{Button, Icon, IconType, JavaInstallManager, OverlayPopup};
|
||||||
use crate::hooks::{use_dispatch, use_notifications_snapshot};
|
use crate::hooks::{use_dispatch, use_notifications_snapshot};
|
||||||
|
|
@ -65,9 +65,8 @@ impl Component for JavaPromptOverlay {
|
||||||
.pick_folder()
|
.pick_folder()
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
dispatch.answer_prompt(
|
dispatch
|
||||||
Answer::new(JAVA_CHOICE_FOLDER).with_folder(handle.path()),
|
.answer_prompt(Answer::new(JAVA_CHOICE_FOLDER).with_folder(handle.path()));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -124,9 +123,7 @@ impl Component for JavaPromptOverlay {
|
||||||
.child(
|
.child(
|
||||||
Button::new()
|
Button::new()
|
||||||
.secondary()
|
.secondary()
|
||||||
.on_press(move |_| {
|
.on_press(move |_| cancel.dismiss_prompt())
|
||||||
cancel.dismiss_prompt()
|
|
||||||
})
|
|
||||||
.text("Cancel"),
|
.text("Cancel"),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
|
|
|
||||||
|
|
@ -195,8 +195,7 @@ impl ElementExt for ScaledImageElement {
|
||||||
let image = &self.image_handle.image;
|
let image = &self.image_handle.image;
|
||||||
let natural = Size2D::new(image.width() as f32, image.height() as f32);
|
let natural = Size2D::new(image.width() as f32, image.height() as f32);
|
||||||
|
|
||||||
let available =
|
let available = (*context.area_size - context.torin_node.margin.into()).max(Size2D::zero());
|
||||||
(*context.area_size - context.torin_node.margin.into()).max(Size2D::zero());
|
|
||||||
|
|
||||||
let ratio = (available.width / natural.width).min(1.);
|
let ratio = (available.width / natural.width).min(1.);
|
||||||
let ratio = if ratio.is_finite() && ratio > 0. {
|
let ratio = if ratio.is_finite() && ratio > 0. {
|
||||||
|
|
@ -256,8 +255,8 @@ impl ElementExt for ScaledImageElement {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use freya::prelude::Bytes;
|
|
||||||
use freya::engine::prelude::AlphaType;
|
use freya::engine::prelude::AlphaType;
|
||||||
|
use freya::prelude::Bytes;
|
||||||
use freya_testing::prelude::*;
|
use freya_testing::prelude::*;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
|
|
@ -115,4 +115,3 @@ impl Component for Markdown {
|
||||||
self.key.clone().or(self.default_key())
|
self.key.clone().or(self.default_key())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -274,7 +274,10 @@ impl Walker {
|
||||||
} else {
|
} else {
|
||||||
std::mem::take(&mut self.image_alt)
|
std::mem::take(&mut self.image_alt)
|
||||||
};
|
};
|
||||||
self.place(Inline::Image { url, alt }, |url, alt| Block::Image { url, alt });
|
self.place(Inline::Image { url, alt }, |url, alt| Block::Image {
|
||||||
|
url,
|
||||||
|
alt,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
TagEnd::Link => {
|
TagEnd::Link => {
|
||||||
self.in_link = false;
|
self.in_link = false;
|
||||||
|
|
@ -488,18 +491,27 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn inline_html_keeps_the_text_it_wrapped() {
|
fn inline_html_keeps_the_text_it_wrapped() {
|
||||||
assert_eq!(paragraph_text(&blocks("a <b>bold</b> word")[0]), "a bold word");
|
assert_eq!(
|
||||||
|
paragraph_text(&blocks("a <b>bold</b> word")[0]),
|
||||||
|
"a bold word"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_html_keeps_its_text() {
|
fn block_html_keeps_its_text() {
|
||||||
assert_eq!(paragraph_text(&blocks("<div>\nhello\n</div>")[0]).trim(), "hello");
|
assert_eq!(
|
||||||
|
paragraph_text(&blocks("<div>\nhello\n</div>")[0]).trim(),
|
||||||
|
"hello"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn images_arrive_as_paragraph_content() {
|
fn images_arrive_as_paragraph_content() {
|
||||||
assert_eq!(paragraph_text(&blocks("")[0]), "[img:alt]");
|
assert_eq!(paragraph_text(&blocks("")[0]), "[img:alt]");
|
||||||
assert_eq!(paragraph_text(&blocks("see  here")[0]), "see [img:alt] here");
|
assert_eq!(
|
||||||
|
paragraph_text(&blocks("see  here")[0]),
|
||||||
|
"see [img:alt] here"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -530,7 +542,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tables_split_head_from_body() {
|
fn tables_split_head_from_body() {
|
||||||
let Block::Table { headers, rows } = &blocks("| a | b |\n| - | - |\n| 1 | 2 |\n| 3 | 4 |")[0]
|
let Block::Table { headers, rows } =
|
||||||
|
&blocks("| a | b |\n| - | - |\n| 1 | 2 |\n| 3 | 4 |")[0]
|
||||||
else {
|
else {
|
||||||
panic!("expected a table");
|
panic!("expected a table");
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,15 @@ use crate::hooks::LinkConfirmState;
|
||||||
|
|
||||||
pub fn render_block(block: &Block, key: usize, style: &MarkdownStyle) -> Element {
|
pub fn render_block(block: &Block, key: usize, style: &MarkdownStyle) -> Element {
|
||||||
match block {
|
match block {
|
||||||
Block::Heading { level, spans } => {
|
Block::Heading { level, spans } => render_spans(
|
||||||
render_spans(spans, style.heading_size(*level), style.color, style.color_code)
|
spans,
|
||||||
.font_weight(FontWeight::BOLD)
|
style.heading_size(*level),
|
||||||
.key(key)
|
style.color,
|
||||||
.into()
|
style.color_code,
|
||||||
}
|
)
|
||||||
|
.font_weight(FontWeight::BOLD)
|
||||||
|
.key(key)
|
||||||
|
.into(),
|
||||||
Block::Paragraph { content } => render_content(content, style.paragraph_size, style)
|
Block::Paragraph { content } => render_content(content, style.paragraph_size, style)
|
||||||
.key(key)
|
.key(key)
|
||||||
.into(),
|
.into(),
|
||||||
|
|
@ -53,7 +56,9 @@ pub fn render_block(block: &Block, key: usize, style: &MarkdownStyle) -> Element
|
||||||
.alignment(BorderAlignment::Inner),
|
.alignment(BorderAlignment::Inner),
|
||||||
)
|
)
|
||||||
.background(style.background_blockquote)
|
.background(style.background_blockquote)
|
||||||
.child(render_content(content, style.paragraph_size, style).font_slant(FontSlant::Italic))
|
.child(
|
||||||
|
render_content(content, style.paragraph_size, style).font_slant(FontSlant::Italic),
|
||||||
|
)
|
||||||
.into(),
|
.into(),
|
||||||
Block::Rule => rect()
|
Block::Rule => rect()
|
||||||
.key(key)
|
.key(key)
|
||||||
|
|
@ -215,12 +220,7 @@ impl KeyExt for MarkdownLink {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MarkdownLink {
|
impl MarkdownLink {
|
||||||
fn new(
|
fn new(url: String, title: Option<String>, content: Vec<Inline>, style: MarkdownStyle) -> Self {
|
||||||
url: String,
|
|
||||||
title: Option<String>,
|
|
||||||
content: Vec<Inline>,
|
|
||||||
style: MarkdownStyle,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
url,
|
url,
|
||||||
title,
|
title,
|
||||||
|
|
@ -242,18 +242,24 @@ impl Component for MarkdownLink {
|
||||||
let mut text = paragraph().font_size(self.style.paragraph_size);
|
let mut text = paragraph().font_size(self.style.paragraph_size);
|
||||||
for item in &self.content {
|
for item in &self.content {
|
||||||
text = match item {
|
text = match item {
|
||||||
Inline::Span(span) => {
|
Inline::Span(span) => text.span(styled_span(
|
||||||
text.span(styled_span(span, self.style.color_link, self.style.color_code))
|
span,
|
||||||
}
|
self.style.color_link,
|
||||||
|
self.style.color_code,
|
||||||
|
)),
|
||||||
Inline::Image { url, alt } => {
|
Inline::Image { url, alt } => {
|
||||||
text.child(MarkdownImage::new(url.clone(), alt.clone()))
|
text.child(MarkdownImage::new(url.clone(), alt.clone()))
|
||||||
}
|
}
|
||||||
Inline::Link { content, .. } => content.iter().fold(text, |text, item| match item {
|
Inline::Link { content, .. } => {
|
||||||
Inline::Span(span) => {
|
content.iter().fold(text, |text, item| match item {
|
||||||
text.span(styled_span(span, self.style.color_link, self.style.color_code))
|
Inline::Span(span) => text.span(styled_span(
|
||||||
}
|
span,
|
||||||
_ => text,
|
self.style.color_link,
|
||||||
}),
|
self.style.color_code,
|
||||||
|
)),
|
||||||
|
_ => text,
|
||||||
|
})
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
|
use crate::state::LoginProgress;
|
||||||
use freya::prelude::*;
|
use freya::prelude::*;
|
||||||
use freya::query::{MutationCapability, MutationStateData, UseMutation};
|
use freya::query::{MutationCapability, MutationStateData, UseMutation};
|
||||||
use freya::text_edit::Clipboard;
|
use freya::text_edit::Clipboard;
|
||||||
use oneclient_core::LauncherError;
|
|
||||||
use oneclient_auth::{AuthErrorGuidance, MicrosoftLoginSession};
|
use oneclient_auth::{AuthErrorGuidance, MicrosoftLoginSession};
|
||||||
use crate::state::LoginProgress;
|
use oneclient_core::LauncherError;
|
||||||
|
|
||||||
use crate::components::{Button, Icon, IconType, OverlayPopup};
|
use crate::components::{Button, Icon, IconType, OverlayPopup};
|
||||||
use crate::hooks::{
|
use crate::hooks::{
|
||||||
|
|
@ -225,10 +225,7 @@ pub(crate) fn login_dialog(
|
||||||
.spacing(18.)
|
.spacing(18.)
|
||||||
.child(browser_dialog_body(auth_url))
|
.child(browser_dialog_body(auth_url))
|
||||||
.child(dialog_divider())
|
.child(dialog_divider())
|
||||||
.child(device_code_dialog_body(
|
.child(device_code_dialog_body(user_code, verification_uri))
|
||||||
user_code,
|
|
||||||
verification_uri,
|
|
||||||
))
|
|
||||||
.child(status_row(status, error))
|
.child(status_row(status, error))
|
||||||
.maybe_child(guidance.map(guidance_block)),
|
.maybe_child(guidance.map(guidance_block)),
|
||||||
),
|
),
|
||||||
|
|
@ -314,17 +311,13 @@ fn status_row(status: Option<LoginProgress>, error: Option<String>) -> impl Into
|
||||||
}
|
}
|
||||||
|
|
||||||
fn guidance_block(guidance: AuthErrorGuidance) -> impl IntoElement {
|
fn guidance_block(guidance: AuthErrorGuidance) -> impl IntoElement {
|
||||||
let mut steps = rect()
|
let mut steps = rect().vertical().width(Size::fill()).spacing(6.).child(
|
||||||
.vertical()
|
label()
|
||||||
.width(Size::fill())
|
.text("What you can do:")
|
||||||
.spacing(6.)
|
.font_size(12.)
|
||||||
.child(
|
.font_weight(FontWeight::SEMI_BOLD)
|
||||||
label()
|
.color(colors::fg_primary()),
|
||||||
.text("What you can do:")
|
);
|
||||||
.font_size(12.)
|
|
||||||
.font_weight(FontWeight::SEMI_BOLD)
|
|
||||||
.color(colors::fg_primary()),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (index, step) in guidance.steps_to_fix.into_iter().enumerate() {
|
for (index, step) in guidance.steps_to_fix.into_iter().enumerate() {
|
||||||
steps = steps.child(
|
steps = steps.child(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
mod account_switcher;
|
mod account_switcher;
|
||||||
mod control_center;
|
|
||||||
mod active_cluster_panel;
|
mod active_cluster_panel;
|
||||||
mod avatar;
|
mod avatar;
|
||||||
mod button;
|
mod button;
|
||||||
|
|
@ -8,24 +7,25 @@ mod checkbox;
|
||||||
mod cluster_landscape_art;
|
mod cluster_landscape_art;
|
||||||
mod cluster_update_popup;
|
mod cluster_update_popup;
|
||||||
mod context_menu;
|
mod context_menu;
|
||||||
|
mod control_center;
|
||||||
mod date_range_picker;
|
mod date_range_picker;
|
||||||
mod optional_mods_popup;
|
|
||||||
mod dropdown;
|
mod dropdown;
|
||||||
mod dynamic_art;
|
mod dynamic_art;
|
||||||
mod file_drop;
|
mod file_drop;
|
||||||
|
mod generic_prompt;
|
||||||
mod icons;
|
mod icons;
|
||||||
mod java_install_manager;
|
mod java_install_manager;
|
||||||
mod generic_prompt;
|
|
||||||
mod java_prompt;
|
mod java_prompt;
|
||||||
mod microsoft_java_prompt;
|
|
||||||
mod link_confirm;
|
mod link_confirm;
|
||||||
mod local_image;
|
mod local_image;
|
||||||
mod log_viewer;
|
mod log_viewer;
|
||||||
mod markdown;
|
mod markdown;
|
||||||
mod memory_field;
|
mod memory_field;
|
||||||
|
mod microsoft_java_prompt;
|
||||||
mod microsoft_login;
|
mod microsoft_login;
|
||||||
mod navbar;
|
mod navbar;
|
||||||
mod notifications;
|
mod notifications;
|
||||||
|
mod optional_mods_popup;
|
||||||
mod overlay_popup;
|
mod overlay_popup;
|
||||||
mod package_row;
|
mod package_row;
|
||||||
mod package_update_popup;
|
mod package_update_popup;
|
||||||
|
|
@ -44,11 +44,10 @@ mod toasts;
|
||||||
mod toggle;
|
mod toggle;
|
||||||
mod tooltip;
|
mod tooltip;
|
||||||
mod update_prompt;
|
mod update_prompt;
|
||||||
mod version_card;
|
|
||||||
pub mod upload_mclogs;
|
pub mod upload_mclogs;
|
||||||
|
mod version_card;
|
||||||
|
|
||||||
pub use account_switcher::AccountSwitcher;
|
pub use account_switcher::AccountSwitcher;
|
||||||
pub use control_center::ControlCenter;
|
|
||||||
pub use active_cluster_panel::ActiveClusterPanel;
|
pub use active_cluster_panel::ActiveClusterPanel;
|
||||||
pub use avatar::Avatar;
|
pub use avatar::Avatar;
|
||||||
pub use button::{Button, ButtonSize, ButtonVariant, link_button, open_folder_button};
|
pub use button::{Button, ButtonSize, ButtonVariant, link_button, open_folder_button};
|
||||||
|
|
@ -57,29 +56,30 @@ pub use checkbox::checkbox_labeled;
|
||||||
pub use cluster_landscape_art::ClusterLandscapeArt;
|
pub use cluster_landscape_art::ClusterLandscapeArt;
|
||||||
pub use cluster_update_popup::ClusterUpdatePopup;
|
pub use cluster_update_popup::ClusterUpdatePopup;
|
||||||
pub use context_menu::ContextMenu;
|
pub use context_menu::ContextMenu;
|
||||||
|
pub use control_center::ControlCenter;
|
||||||
pub use date_range_picker::{DateRange, DateRangePicker};
|
pub use date_range_picker::{DateRange, DateRangePicker};
|
||||||
pub use optional_mods_popup::OptionalModsPopup;
|
|
||||||
pub use dropdown::Dropdown;
|
pub use dropdown::Dropdown;
|
||||||
pub use dynamic_art::{ART_PREVIEW_EDGE, DynamicArt};
|
pub use dynamic_art::{ART_PREVIEW_EDGE, DynamicArt};
|
||||||
pub use file_drop::FileDropOverlay;
|
pub use file_drop::FileDropOverlay;
|
||||||
|
pub use generic_prompt::GenericPromptOverlay;
|
||||||
pub use icons::{Icon, IconTint, IconType};
|
pub use icons::{Icon, IconTint, IconType};
|
||||||
pub use java_install_manager::JavaInstallManager;
|
pub use java_install_manager::JavaInstallManager;
|
||||||
pub use generic_prompt::GenericPromptOverlay;
|
|
||||||
pub use java_prompt::JavaPromptOverlay;
|
pub use java_prompt::JavaPromptOverlay;
|
||||||
pub use microsoft_java_prompt::MicrosoftJavaPromptOverlay;
|
|
||||||
pub use link_confirm::ConfirmLinkOverlay;
|
pub use link_confirm::ConfirmLinkOverlay;
|
||||||
pub use local_image::LocalImage;
|
pub use local_image::LocalImage;
|
||||||
pub use markdown::{Markdown, MarkdownStyle};
|
|
||||||
pub use log_viewer::LogViewer;
|
pub use log_viewer::LogViewer;
|
||||||
|
pub use markdown::{Markdown, MarkdownStyle};
|
||||||
pub use memory_field::memory_field;
|
pub use memory_field::memory_field;
|
||||||
pub use microsoft_login::use_microsoft_login;
|
pub use microsoft_java_prompt::MicrosoftJavaPromptOverlay;
|
||||||
pub(crate) use microsoft_login::login_dialog;
|
pub(crate) use microsoft_login::login_dialog;
|
||||||
|
pub use microsoft_login::use_microsoft_login;
|
||||||
pub(crate) use navbar::window_controls;
|
pub(crate) use navbar::window_controls;
|
||||||
pub use navbar::{AppNavbar, OnboardingNavbar};
|
pub use navbar::{AppNavbar, OnboardingNavbar};
|
||||||
pub use notifications::NotificationCenter;
|
pub use notifications::NotificationCenter;
|
||||||
|
pub use optional_mods_popup::OptionalModsPopup;
|
||||||
pub use overlay_popup::{OVERLAY_BASE_LEVEL, OVERLAY_MAX_LEVEL, OverlayPopup, overlay_is_topmost};
|
pub use overlay_popup::{OVERLAY_BASE_LEVEL, OVERLAY_MAX_LEVEL, OverlayPopup, overlay_is_topmost};
|
||||||
pub use package_row::{CardLayout, PackageEntry, PackageRow, provider_badge};
|
|
||||||
pub(crate) use package_row::{CARD_GRID_H, GRID_GAP, GRID_MIN_W, grid_card, package_icon};
|
pub(crate) use package_row::{CARD_GRID_H, GRID_GAP, GRID_MIN_W, grid_card, package_icon};
|
||||||
|
pub use package_row::{CardLayout, PackageEntry, PackageRow, provider_badge};
|
||||||
pub use package_update_popup::PackageUpdatePopup;
|
pub use package_update_popup::PackageUpdatePopup;
|
||||||
pub use pagination::Pagination;
|
pub use pagination::Pagination;
|
||||||
pub use player_model::PlayerModel;
|
pub use player_model::PlayerModel;
|
||||||
|
|
|
||||||
|
|
@ -125,10 +125,10 @@ fn navbar_center(is_small: bool) -> impl IntoElement {
|
||||||
})
|
})
|
||||||
.cross_align(Alignment::Center)
|
.cross_align(Alignment::Center)
|
||||||
.spacing(if is_small {
|
.spacing(if is_small {
|
||||||
NAV_LINK_SPACING_PX
|
NAV_LINK_SPACING_PX
|
||||||
} else {
|
} else {
|
||||||
NAV_LINK_SPACING_PX / 2.
|
NAV_LINK_SPACING_PX / 2.
|
||||||
})
|
})
|
||||||
.child(NavLink {
|
.child(NavLink {
|
||||||
active: route == Route::Home {},
|
active: route == Route::Home {},
|
||||||
target: NavTarget::Route(Route::Home {}),
|
target: NavTarget::Route(Route::Home {}),
|
||||||
|
|
@ -308,7 +308,7 @@ impl Component for NavbarRight {
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
super::navbar_button()
|
super::navbar_button()
|
||||||
.overflow(Overflow::None)
|
.overflow(Overflow::None)
|
||||||
.tooltip("Control Center")
|
.tooltip("Control Center")
|
||||||
.padding(0.0)
|
.padding(0.0)
|
||||||
.on_press(open_control_center)
|
.on_press(open_control_center)
|
||||||
|
|
@ -323,11 +323,7 @@ fn avatar_with_gear(uuid: String) -> impl IntoElement {
|
||||||
.width(Size::px(28.))
|
.width(Size::px(28.))
|
||||||
.height(Size::px(28.))
|
.height(Size::px(28.))
|
||||||
.center()
|
.center()
|
||||||
.child(
|
.child(Avatar::new(uuid).width(Size::px(28.)).height(Size::px(28.)))
|
||||||
Avatar::new(uuid)
|
|
||||||
.width(Size::px(28.))
|
|
||||||
.height(Size::px(28.)),
|
|
||||||
)
|
|
||||||
.child(
|
.child(
|
||||||
rect()
|
rect()
|
||||||
.position(Position::new_absolute().bottom(-7.).right(-7.))
|
.position(Position::new_absolute().bottom(-7.).right(-7.))
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ use freya::{
|
||||||
use oneclient_events::Level;
|
use oneclient_events::Level;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ui::{divider, relative_time},
|
|
||||||
components::{Button, ButtonVariant, Icon, IconType, OverlayPopup, ScrollArea, progress_track},
|
components::{Button, ButtonVariant, Icon, IconType, OverlayPopup, ScrollArea, progress_track},
|
||||||
hooks::{use_dispatch, use_notifications_snapshot},
|
hooks::{use_dispatch, use_notifications_snapshot},
|
||||||
notifications::{InboxEntry, NotificationActionKind},
|
notifications::{InboxEntry, NotificationActionKind},
|
||||||
theme::colors,
|
theme::colors,
|
||||||
transfer::TransferStats,
|
transfer::TransferStats,
|
||||||
|
ui::{divider, relative_time},
|
||||||
utils::{format_durations, format_size},
|
utils::{format_durations, format_size},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -212,7 +212,9 @@ fn tasks_section(entry: &InboxEntry, mut expanded: State<bool>) -> impl IntoElem
|
||||||
let now = !*expanded.peek();
|
let now = !*expanded.peek();
|
||||||
expanded.set(now);
|
expanded.set(now);
|
||||||
})
|
})
|
||||||
.child(ChevronToggle { expanded: is_expanded })
|
.child(ChevronToggle {
|
||||||
|
expanded: is_expanded,
|
||||||
|
})
|
||||||
.child(
|
.child(
|
||||||
label()
|
label()
|
||||||
.text(if is_expanded {
|
.text(if is_expanded {
|
||||||
|
|
@ -525,4 +527,3 @@ fn level_color(level: &Level) -> Color {
|
||||||
Level::Error => colors::danger(),
|
Level::Error => colors::danger(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ use oneclient_content::packages::{CachedPackageMeta, ProviderId};
|
||||||
use oneclient_core::{BrowserPackageUpdate, ProfileUpdate};
|
use oneclient_core::{BrowserPackageUpdate, ProfileUpdate};
|
||||||
use oneclient_db::models::ClusterId;
|
use oneclient_db::models::ClusterId;
|
||||||
|
|
||||||
use crate::components::{Button, Dropdown, Icon, IconType, OverlayPopup, ScrollArea, checkbox_labeled};
|
use crate::components::{
|
||||||
|
Button, Dropdown, Icon, IconType, OverlayPopup, ScrollArea, checkbox_labeled,
|
||||||
|
};
|
||||||
use crate::hooks::{
|
use crate::hooks::{
|
||||||
package_meta_batch, use_dispatch, use_notifications_snapshot, use_package_meta_batch,
|
package_meta_batch, use_dispatch, use_notifications_snapshot, use_package_meta_batch,
|
||||||
};
|
};
|
||||||
|
|
@ -210,9 +212,7 @@ fn content(
|
||||||
.main_align(Alignment::Start)
|
.main_align(Alignment::Start)
|
||||||
.cross_align(Alignment::Center)
|
.cross_align(Alignment::Center)
|
||||||
.spacing(6.)
|
.spacing(6.)
|
||||||
.child(
|
.child(checkbox_labeled(dont_show_again, "Don't show again?")),
|
||||||
checkbox_labeled(dont_show_again, "Don't show again?")
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
rect()
|
rect()
|
||||||
|
|
@ -267,7 +267,7 @@ fn content(
|
||||||
})
|
})
|
||||||
.child(Icon::new(IconType::DownloadCloud02).size(15.))
|
.child(Icon::new(IconType::DownloadCloud02).size(15.))
|
||||||
.text("Proceed"),
|
.text("Proceed"),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,26 +52,24 @@ impl Component for Pagination {
|
||||||
let current = *page.read();
|
let current = *page.read();
|
||||||
let last = total_pages.saturating_sub(1);
|
let last = total_pages.saturating_sub(1);
|
||||||
|
|
||||||
let icon_btn = move |target: usize,
|
let icon_btn =
|
||||||
available: bool,
|
move |target: usize, available: bool, icon: IconType, tooltip: &'static str| {
|
||||||
icon: IconType,
|
let mut page = page;
|
||||||
tooltip: &'static str| {
|
let enabled = available && interactive;
|
||||||
let mut page = page;
|
Button::new()
|
||||||
let enabled = available && interactive;
|
.secondary()
|
||||||
Button::new()
|
.icon()
|
||||||
.secondary()
|
.enabled(enabled)
|
||||||
.icon()
|
.tooltip(tooltip)
|
||||||
.enabled(enabled)
|
.tooltip_placement(TooltipPlacement::Top)
|
||||||
.tooltip(tooltip)
|
.on_press(move |_| page.set(target))
|
||||||
.tooltip_placement(TooltipPlacement::Top)
|
.child(Icon::new(icon).size(14.).color(if enabled {
|
||||||
.on_press(move |_| page.set(target))
|
colors::fg_primary()
|
||||||
.child(Icon::new(icon).size(14.).color(if enabled {
|
} else {
|
||||||
colors::fg_primary()
|
colors::fg_secondary().with_a(90)
|
||||||
} else {
|
}))
|
||||||
colors::fg_secondary().with_a(90)
|
.into_element()
|
||||||
}))
|
};
|
||||||
.into_element()
|
|
||||||
};
|
|
||||||
|
|
||||||
let num_btn = move |target: usize| {
|
let num_btn = move |target: usize| {
|
||||||
let mut page = page;
|
let mut page = page;
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,8 @@ impl Component for RecentsRow {
|
||||||
|
|
||||||
let sorted: Vec<Cluster> = sort_clusters_for_home(clusters);
|
let sorted: Vec<Cluster> = sort_clusters_for_home(clusters);
|
||||||
|
|
||||||
let slots = recent_card_slots_for_window(
|
let slots =
|
||||||
root_size.read().width,
|
recent_card_slots_for_window(root_size.read().width, *scale_factor.read() as f32);
|
||||||
*scale_factor.read() as f32,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Budget off cluster count not slot count an offset-only change makes the layout
|
// Budget off cluster count not slot count an offset-only change makes the layout
|
||||||
// engine reuse a card's cached area freezing survivors at pre-resize widths
|
// engine reuse a card's cached area freezing survivors at pre-resize widths
|
||||||
|
|
@ -255,7 +253,10 @@ impl Component for ClusterCard {
|
||||||
.height(Size::fill())
|
.height(Size::fill())
|
||||||
.position(Position::new_absolute())
|
.position(Position::new_absolute())
|
||||||
.layer(Layer::Relative(1))
|
.layer(Layer::Relative(1))
|
||||||
.child(DynamicArt::for_cluster(&self.cluster).max_edge(ART_PREVIEW_EDGE)),
|
.child(
|
||||||
|
DynamicArt::for_cluster(&self.cluster)
|
||||||
|
.max_edge(ART_PREVIEW_EDGE),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
rect()
|
rect()
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,11 @@ pub(crate) fn normalize_wheel_delta(delta: f64, scale_factor: f64) -> f32 {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FREYA_LINE_SPEED: f64 = 53.0;
|
const FREYA_LINE_SPEED: f64 = 53.0;
|
||||||
const FREYA_PIXEL_SPEED: f64 = 2.0;
|
const FREYA_PIXEL_SPEED: f64 = 2.0;
|
||||||
const LINE_DELTA_TOLERANCE: f64 = 1e-6;
|
const LINE_DELTA_TOLERANCE: f64 = 1e-6;
|
||||||
const LINE_SCROLL: f64 = 53.0;
|
const LINE_SCROLL: f64 = 53.0;
|
||||||
const PIXEL_SCROLL: f64 = 1.0;
|
const PIXEL_SCROLL: f64 = 1.0;
|
||||||
|
|
||||||
let lines = (delta / FREYA_LINE_SPEED).round();
|
let lines = (delta / FREYA_LINE_SPEED).round();
|
||||||
if lines != 0.0 && (delta - lines * FREYA_LINE_SPEED).abs() <= LINE_DELTA_TOLERANCE {
|
if lines != 0.0 && (delta - lines * FREYA_LINE_SPEED).abs() <= LINE_DELTA_TOLERANCE {
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@ use freya::{
|
||||||
use oneclient_events::Level;
|
use oneclient_events::Level;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ui::{divider},
|
|
||||||
components::{Button, ButtonSize, Icon, IconType, TooltipPlacement, progress_track},
|
components::{Button, ButtonSize, Icon, IconType, TooltipPlacement, progress_track},
|
||||||
hooks::{use_dispatch, use_notifications_snapshot},
|
hooks::{use_dispatch, use_notifications_snapshot},
|
||||||
motion::use_animations_enabled,
|
motion::use_animations_enabled,
|
||||||
notifications::{InboxEntry, NotificationActionKind},
|
notifications::{InboxEntry, NotificationActionKind},
|
||||||
theme::colors,
|
theme::colors,
|
||||||
|
ui::divider,
|
||||||
};
|
};
|
||||||
|
|
||||||
const TOAST_W: f32 = 300.;
|
const TOAST_W: f32 = 300.;
|
||||||
|
|
@ -339,12 +339,7 @@ fn close_button(dispatch: crate::Actions, id: u64) -> impl IntoElement {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn message_card(
|
fn message_card(entry: &InboxEntry, dispatch: crate::Actions, id: u64, ttl_pct: f32) -> Rect {
|
||||||
entry: &InboxEntry,
|
|
||||||
dispatch: crate::Actions,
|
|
||||||
id: u64,
|
|
||||||
ttl_pct: f32,
|
|
||||||
) -> Rect {
|
|
||||||
toast_shell(entry)
|
toast_shell(entry)
|
||||||
.child(
|
.child(
|
||||||
rect()
|
rect()
|
||||||
|
|
|
||||||
|
|
@ -287,11 +287,19 @@ fn place(anchor: AnchorRect, placement: TooltipPlacement, size: Size2D) -> (f32,
|
||||||
let (x, y) = match placement {
|
let (x, y) = match placement {
|
||||||
TooltipPlacement::Top => (
|
TooltipPlacement::Top => (
|
||||||
centered_x,
|
centered_x,
|
||||||
if fits_above || !fits_below { above } else { below },
|
if fits_above || !fits_below {
|
||||||
|
above
|
||||||
|
} else {
|
||||||
|
below
|
||||||
|
},
|
||||||
),
|
),
|
||||||
TooltipPlacement::Bottom => (
|
TooltipPlacement::Bottom => (
|
||||||
centered_x,
|
centered_x,
|
||||||
if fits_below || !fits_above { below } else { above },
|
if fits_below || !fits_above {
|
||||||
|
below
|
||||||
|
} else {
|
||||||
|
above
|
||||||
|
},
|
||||||
),
|
),
|
||||||
TooltipPlacement::Left => (
|
TooltipPlacement::Left => (
|
||||||
if fits_before || !fits_after {
|
if fits_before || !fits_after {
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,7 @@ pub struct UploadToMclogs {
|
||||||
|
|
||||||
impl UploadToMclogs {
|
impl UploadToMclogs {
|
||||||
pub fn new(has_log: bool, confirm: State<Option<Confirm>>) -> Self {
|
pub fn new(has_log: bool, confirm: State<Option<Confirm>>) -> Self {
|
||||||
Self {
|
Self { has_log, confirm }
|
||||||
has_log,
|
|
||||||
confirm
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,6 @@ pub const WINDOW_APP_ID: &str = "oneclient_app";
|
||||||
pub const WINDOW_TITLE: &str = "OneClient";
|
pub const WINDOW_TITLE: &str = "OneClient";
|
||||||
|
|
||||||
pub const UPDATER_PUBKEY: &str = "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDFGODk3MkMyMjg0MjFDMDUKUldRRkhFSW93bktKSHpkWjNEMXNzaDVINVpCTU8xSnhuK2RnV0dTZ2FkcFJWbG1zUkhGYTNjaUkK";
|
pub const UPDATER_PUBKEY: &str = "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDFGODk3MkMyMjg0MjFDMDUKUldRRkhFSW93bktKSHpkWjNEMXNzaDVINVpCTU8xSnhuK2RnV0dTZ2FkcFJWbG1zUkhGYTNjaUkK";
|
||||||
pub const UPDATER_ENDPOINT: &str = "https://github.com/Polyfrost/OneLauncher/releases/latest/download/latest.json";
|
pub const UPDATER_ENDPOINT: &str =
|
||||||
pub const RELEASES_URL: &str = "https://github.com/Polyfrost/OneLauncher/releases/latest";
|
"https://github.com/Polyfrost/OneLauncher/releases/latest/download/latest.json";
|
||||||
|
pub const RELEASES_URL: &str = "https://github.com/Polyfrost/OneLauncher/releases/latest";
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,10 @@ impl EventPump {
|
||||||
guard.game.stages.insert(cluster_id, stage);
|
guard.game.stages.insert(cluster_id, stage);
|
||||||
if stage == LaunchStage::Checking {
|
if stage == LaunchStage::Checking {
|
||||||
guard.game.error = None;
|
guard.game.error = None;
|
||||||
guard.game.logs.insert(cluster_id, std::sync::Arc::new(Vec::new()));
|
guard
|
||||||
|
.game
|
||||||
|
.logs
|
||||||
|
.insert(cluster_id, std::sync::Arc::new(Vec::new()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (cluster_id, line) in logs {
|
for (cluster_id, line) in logs {
|
||||||
|
|
@ -264,7 +267,9 @@ fn reconcile(
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for id in &want {
|
for id in &want {
|
||||||
armed.entry(*id).or_insert_with(|| ToastTimer::armed(paused));
|
armed
|
||||||
|
.entry(*id)
|
||||||
|
.or_insert_with(|| ToastTimer::armed(paused));
|
||||||
}
|
}
|
||||||
armed.retain(|id, _| want.contains(id));
|
armed.retain(|id, _| want.contains(id));
|
||||||
}
|
}
|
||||||
|
|
@ -358,10 +363,7 @@ pub async fn start_launcher(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_startup_failure(
|
pub fn report_startup_failure(station: &RadioStation<AppState, AppChannel>, err: &anyhow::Error) {
|
||||||
station: &RadioStation<AppState, AppChannel>,
|
|
||||||
err: &anyhow::Error,
|
|
||||||
) {
|
|
||||||
let message = err.to_string();
|
let message = err.to_string();
|
||||||
tracing::error!("launcher init failed: {err:#}");
|
tracing::error!("launcher init failed: {err:#}");
|
||||||
|
|
||||||
|
|
@ -379,7 +381,9 @@ pub fn report_startup_failure(
|
||||||
|
|
||||||
let mut guard = station.write_channel(AppChannel::Notifications);
|
let mut guard = station.write_channel(AppChannel::Notifications);
|
||||||
let AppState {
|
let AppState {
|
||||||
notifications, inbox, ..
|
notifications,
|
||||||
|
inbox,
|
||||||
|
..
|
||||||
} = &mut **guard;
|
} = &mut **guard;
|
||||||
notifications.dispatch(
|
notifications.dispatch(
|
||||||
inbox,
|
inbox,
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,10 @@ pub fn bundle_slug(folder: &str) -> String {
|
||||||
pub fn info_plist(name: &str, executable: &str, folder: &str, icon: Option<&str>) -> String {
|
pub fn info_plist(name: &str, executable: &str, folder: &str, icon: Option<&str>) -> String {
|
||||||
let display = xml_escape(&single_line(name));
|
let display = xml_escape(&single_line(name));
|
||||||
let icon_entry = icon.map_or_else(String::new, |icon| {
|
let icon_entry = icon.map_or_else(String::new, |icon| {
|
||||||
format!("\t<key>CFBundleIconFile</key>\n\t<string>{}</string>\n", xml_escape(icon))
|
format!(
|
||||||
|
"\t<key>CFBundleIconFile</key>\n\t<string>{}</string>\n",
|
||||||
|
xml_escape(icon)
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
format!(
|
format!(
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,12 @@ use oneclient_events::{Answer, Level};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::components::IconType;
|
use crate::components::IconType;
|
||||||
use crate::{invalidate_java_queries, launcher};
|
|
||||||
use crate::notifications::{
|
use crate::notifications::{
|
||||||
ClusterUpdateSummary, NotificationAction, NotificationSpec, OptionalModRef, OptionalModsGroup,
|
ClusterUpdateSummary, NotificationAction, NotificationSpec, OptionalModRef, OptionalModsGroup,
|
||||||
PackageUpdateGroup, PendingPrompt,
|
PackageUpdateGroup, PendingPrompt,
|
||||||
};
|
};
|
||||||
use crate::state::{AppChannel, AppState, AsyncStatus, RelocationState};
|
use crate::state::{AppChannel, AppState, AsyncStatus, RelocationState};
|
||||||
|
use crate::{invalidate_java_queries, launcher};
|
||||||
|
|
||||||
/// Over-disabling is the cheaper mistake a dead button beats a second game
|
/// Over-disabling is the cheaper mistake a dead button beats a second game
|
||||||
const LAUNCH_HOLD: Duration = Duration::from_secs(2);
|
const LAUNCH_HOLD: Duration = Duration::from_secs(2);
|
||||||
|
|
@ -172,7 +172,10 @@ impl Actions {
|
||||||
|
|
||||||
fn with_engine(&self, mutate: impl FnOnce(&mut AppState)) {
|
fn with_engine(&self, mutate: impl FnOnce(&mut AppState)) {
|
||||||
{
|
{
|
||||||
let mut guard = self.station.clone().write_channel(AppChannel::Notifications);
|
let mut guard = self
|
||||||
|
.station
|
||||||
|
.clone()
|
||||||
|
.write_channel(AppChannel::Notifications);
|
||||||
mutate(&mut guard);
|
mutate(&mut guard);
|
||||||
}
|
}
|
||||||
self.nudge(PumpSignal::Reconcile);
|
self.nudge(PumpSignal::Reconcile);
|
||||||
|
|
@ -196,10 +199,8 @@ impl Actions {
|
||||||
guard.settings.error = None;
|
guard.settings.error = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let loaded = oneclient_core::settings::store::load_settings(Some(
|
let loaded =
|
||||||
&state.services.events,
|
oneclient_core::settings::store::load_settings(Some(&state.services.events)).await;
|
||||||
))
|
|
||||||
.await;
|
|
||||||
let discord_enabled = loaded.discord_enabled;
|
let discord_enabled = loaded.discord_enabled;
|
||||||
*state.settings.write() = loaded.clone();
|
*state.settings.write() = loaded.clone();
|
||||||
state.discord.set_enabled(discord_enabled);
|
state.discord.set_enabled(discord_enabled);
|
||||||
|
|
@ -231,7 +232,10 @@ impl Actions {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mutate_settings(&self, mutate: impl FnOnce(&mut LauncherSettings)) -> Option<LauncherSettings> {
|
fn mutate_settings(
|
||||||
|
&self,
|
||||||
|
mutate: impl FnOnce(&mut LauncherSettings),
|
||||||
|
) -> Option<LauncherSettings> {
|
||||||
let state = launcher::state().ok()?;
|
let state = launcher::state().ok()?;
|
||||||
let updated = {
|
let updated = {
|
||||||
let mut lock = state.settings.write();
|
let mut lock = state.settings.write();
|
||||||
|
|
@ -312,7 +316,7 @@ impl Actions {
|
||||||
self.persist(updated);
|
self.persist(updated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_onboarding(&self) {
|
pub fn reset_onboarding(&self) {
|
||||||
if let Some(updated) = self.mutate_settings(|settings| {
|
if let Some(updated) = self.mutate_settings(|settings| {
|
||||||
settings.seen_onboarding = false;
|
settings.seen_onboarding = false;
|
||||||
|
|
@ -561,14 +565,12 @@ impl Actions {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
events.signal(oneclient_events::Signal::JavaChanged);
|
events.signal(oneclient_events::Signal::JavaChanged);
|
||||||
invalidate_java_queries().await
|
invalidate_java_queries().await
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(err) => events
|
||||||
events
|
|
||||||
.notify("Java install failed")
|
.notify("Java install failed")
|
||||||
.body(err.to_string())
|
.body(err.to_string())
|
||||||
.error()
|
.error()
|
||||||
.send()
|
.send(),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -692,7 +694,11 @@ impl Actions {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dismiss_toast(&self, entry_id: u64) {
|
pub fn dismiss_toast(&self, entry_id: u64) {
|
||||||
self.with_engine(|state| state.notifications.dismiss_toast(&mut state.inbox, entry_id));
|
self.with_engine(|state| {
|
||||||
|
state
|
||||||
|
.notifications
|
||||||
|
.dismiss_toast(&mut state.inbox, entry_id)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mark_notification_read(&self, entry_id: u64) {
|
pub fn mark_notification_read(&self, entry_id: u64) {
|
||||||
|
|
@ -779,10 +785,8 @@ impl Actions {
|
||||||
};
|
};
|
||||||
let content = state.services.content();
|
let content = state.services.content();
|
||||||
let events = state.services.events.clone();
|
let events = state.services.events.clone();
|
||||||
let session = oneclient_events::GroupedProgressSession::start(
|
let session =
|
||||||
&events,
|
oneclient_events::GroupedProgressSession::start(&events, "Adding optional mods");
|
||||||
"Adding optional mods",
|
|
||||||
);
|
|
||||||
let opt_in = session.child(
|
let opt_in = session.child(
|
||||||
"Enabling mods",
|
"Enabling mods",
|
||||||
mods.len() as u64,
|
mods.len() as u64,
|
||||||
|
|
@ -887,9 +891,7 @@ impl Actions {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn proceed_package_updates(&self, chosen: Vec<oneclient_core::BrowserPackageUpdate>) {
|
pub fn proceed_package_updates(&self, chosen: Vec<oneclient_core::BrowserPackageUpdate>) {
|
||||||
self.with_engine(move |state| {
|
self.with_engine(move |state| state.notifications.proceed_package_updates(chosen));
|
||||||
state.notifications.proceed_package_updates(chosen)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close_package_updates(&self) {
|
pub fn close_package_updates(&self) {
|
||||||
|
|
@ -1005,15 +1007,16 @@ impl Actions {
|
||||||
.error = None;
|
.error = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn import_local_file(&self, cluster_id: ClusterId, content_type: ContentType, path: PathBuf) {
|
pub fn import_local_file(
|
||||||
|
&self,
|
||||||
|
cluster_id: ClusterId,
|
||||||
|
content_type: ContentType,
|
||||||
|
path: PathBuf,
|
||||||
|
) {
|
||||||
self.import_local_files(cluster_id, vec![(path, content_type)]);
|
self.import_local_files(cluster_id, vec![(path, content_type)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn import_local_files(
|
pub fn import_local_files(&self, cluster_id: ClusterId, files: Vec<(PathBuf, ContentType)>) {
|
||||||
&self,
|
|
||||||
cluster_id: ClusterId,
|
|
||||||
files: Vec<(PathBuf, ContentType)>,
|
|
||||||
) {
|
|
||||||
if files.is_empty() {
|
if files.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1042,7 +1045,11 @@ impl Actions {
|
||||||
deferred |= live == LiveSync::Deferred;
|
deferred |= live == LiveSync::Deferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
notify_import(&events, &report, deferred && state.games.is_active(cluster_id));
|
notify_import(
|
||||||
|
&events,
|
||||||
|
&report,
|
||||||
|
deferred && state.games.is_active(cluster_id),
|
||||||
|
);
|
||||||
super::invalidate_cluster_queries().await;
|
super::invalidate_cluster_queries().await;
|
||||||
}
|
}
|
||||||
Err(err) => events
|
Err(err) => events
|
||||||
|
|
@ -1353,7 +1360,7 @@ impl Actions {
|
||||||
.relocation = relocation;
|
.relocation = relocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `syncing_bundles` gates every launch button so the readiness check must
|
/// `syncing_bundles` gates every launch button so the readiness check must
|
||||||
/// happen before the flag is raised not inside the task
|
/// happen before the flag is raised not inside the task
|
||||||
pub fn sync_bundles(&self) {
|
pub fn sync_bundles(&self) {
|
||||||
let state = match launcher::state() {
|
let state = match launcher::state() {
|
||||||
|
|
@ -1372,7 +1379,6 @@ impl Actions {
|
||||||
.syncing_bundles = true;
|
.syncing_bundles = true;
|
||||||
|
|
||||||
spawn_forever(async move {
|
spawn_forever(async move {
|
||||||
|
|
||||||
if let Err(err) = state.bundles.sync(&state.services.content()).await {
|
if let Err(err) = state.bundles.sync(&state.services.content()).await {
|
||||||
tracing::error!("bundle catalog sync failed: {err:#}");
|
tracing::error!("bundle catalog sync failed: {err:#}");
|
||||||
}
|
}
|
||||||
|
|
@ -1416,26 +1422,22 @@ impl Actions {
|
||||||
.launcher
|
.launcher
|
||||||
.syncing_bundles = true;
|
.syncing_bundles = true;
|
||||||
|
|
||||||
let synced = match tokio::time::timeout(BUNDLE_SYNC_BUDGET, state.bundles.sync(&content))
|
let synced =
|
||||||
.await
|
match tokio::time::timeout(BUNDLE_SYNC_BUDGET, state.bundles.sync(&content)).await {
|
||||||
{
|
Ok(Ok(_)) => true,
|
||||||
Ok(Ok(_)) => true,
|
Ok(Err(err)) => {
|
||||||
Ok(Err(err)) => {
|
tracing::warn!(
|
||||||
tracing::warn!(
|
cluster_id,
|
||||||
cluster_id,
|
error = %err,
|
||||||
error = %err,
|
"bundle catalog sync failed, launching against the cached catalog"
|
||||||
"bundle catalog sync failed, launching against the cached catalog"
|
);
|
||||||
);
|
false
|
||||||
false
|
}
|
||||||
}
|
Err(_elapsed) => {
|
||||||
Err(_elapsed) => {
|
tracing::debug!(cluster_id, "bundle catalog sync exceeded its launch budget");
|
||||||
tracing::debug!(
|
false
|
||||||
cluster_id,
|
}
|
||||||
"bundle catalog sync exceeded its launch budget"
|
};
|
||||||
);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.station
|
self.station
|
||||||
.clone()
|
.clone()
|
||||||
|
|
@ -1664,7 +1666,9 @@ impl Actions {
|
||||||
|
|
||||||
let (done, wait) = tokio::sync::oneshot::channel();
|
let (done, wait) = tokio::sync::oneshot::channel();
|
||||||
self.with_engine(move |state| {
|
self.with_engine(move |state| {
|
||||||
state.notifications.open_optional_mods(vec![group], Some(done));
|
state
|
||||||
|
.notifications
|
||||||
|
.open_optional_mods(vec![group], Some(done));
|
||||||
state.center_open = false;
|
state.center_open = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1678,7 +1682,9 @@ impl Actions {
|
||||||
let (done, wait) = tokio::sync::oneshot::channel();
|
let (done, wait) = tokio::sync::oneshot::channel();
|
||||||
|
|
||||||
self.with_engine(move |state| {
|
self.with_engine(move |state| {
|
||||||
state.notifications.open_package_updates(vec![group], Some(done));
|
state
|
||||||
|
.notifications
|
||||||
|
.open_package_updates(vec![group], Some(done));
|
||||||
state.center_open = false;
|
state.center_open = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1825,10 +1831,10 @@ fn notify_import(events: &oneclient_events::EventBus, report: &LocalImportReport
|
||||||
[only] => Some(format!("Added {}", only.file_name)),
|
[only] => Some(format!("Added {}", only.file_name)),
|
||||||
rows => Some(format!("Added {} files", rows.len())),
|
rows => Some(format!("Added {} files", rows.len())),
|
||||||
}) else {
|
}) else {
|
||||||
let reason = report
|
let reason = report.failed.first().map_or_else(
|
||||||
.failed
|
|| "Nothing could be read".to_string(),
|
||||||
.first()
|
|(_, err)| err.to_string(),
|
||||||
.map_or_else(|| "Nothing could be read".to_string(), |(_, err)| err.to_string());
|
);
|
||||||
|
|
||||||
events.notify("Import failed").body(reason).error().send();
|
events.notify("Import failed").body(reason).error().send();
|
||||||
return;
|
return;
|
||||||
|
|
@ -2056,7 +2062,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn every_cluster_holding_the_runtime_is_named() {
|
fn every_cluster_holding_the_runtime_is_named() {
|
||||||
let profiles = vec![profile("a", Some(JDK_25)), profile("b", Some(JDK_25))];
|
let profiles = vec![profile("a", Some(JDK_25)), profile("b", Some(JDK_25))];
|
||||||
let clusters = vec![cluster(1, "Alpha", Some("a")), cluster(2, "Beta", Some("b"))];
|
let clusters = vec![
|
||||||
|
cluster(1, "Alpha", Some("a")),
|
||||||
|
cluster(2, "Beta", Some("b")),
|
||||||
|
];
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
clusters_pinned_to_java(&profiles, &clusters, JDK_25),
|
clusters_pinned_to_java(&profiles, &clusters, JDK_25),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
mod actions;
|
||||||
mod active_cluster;
|
mod active_cluster;
|
||||||
mod debounce;
|
mod debounce;
|
||||||
mod actions;
|
|
||||||
mod overlay_claims;
|
mod overlay_claims;
|
||||||
mod queries;
|
mod queries;
|
||||||
mod shortcut_actions;
|
mod shortcut_actions;
|
||||||
|
|
@ -27,43 +27,39 @@ pub use actions::{Actions, NotificationBuilder, PumpSignal};
|
||||||
pub use queries::{
|
pub use queries::{
|
||||||
AddOfflineAccountKeys, BROWSE_PAGE_SIZE, BeginMicrosoftLoginMutation, CachedImageQuery,
|
AddOfflineAccountKeys, BROWSE_PAGE_SIZE, BeginMicrosoftLoginMutation, CachedImageQuery,
|
||||||
CancelMicrosoftLoginKeys, CancelMicrosoftLoginMutation, ClusterAction, ClusterBundles,
|
CancelMicrosoftLoginKeys, CancelMicrosoftLoginMutation, ClusterAction, ClusterBundles,
|
||||||
ClusterLogsQuery, FinishMicrosoftLoginMutation, LogAction, LogContentQuery, MigrationQuery,
|
ClusterLogsQuery, DiscardLeftoversKeys, DiscardLeftoversMutation, FinishMicrosoftLoginMutation,
|
||||||
OnboardingBundlesQuery, RefreshAccountKeys, RemoveAccountKeys, ScreenshotAction,
|
LeftoversQuery, LogAction, LogContentQuery, MigrationQuery, OnboardingBundlesQuery,
|
||||||
SetDefaultAccountKeys, StorageAction, StorageActionMutation, StorageReportQuery, TermsQuery,
|
RefreshAccountKeys, RemoveAccountKeys, ScreenshotAction, SetDefaultAccountKeys, StorageAction,
|
||||||
UploadLogKeys, UploadLogMutation, UseLogAction,
|
StorageActionMutation, StorageReportQuery, TermsQuery, UploadLogKeys, UploadLogMutation,
|
||||||
UseRefreshAccount, UseRemoveAccount, UseScreenshotAction, UseSetDefaultAccount,
|
UseDiscardLeftovers, UseLogAction, UseRefreshAccount, UseRemoveAccount, UseScreenshotAction,
|
||||||
UseStorageAction, UseUploadLog,
|
UseSetDefaultAccount, UseStorageAction, UseUploadLog, VERSIONS_PAGE_SIZE,
|
||||||
VERSIONS_PAGE_SIZE, accounts_have_microsoft, bundle_overrides_map, bundles_with_status_items,
|
accounts_have_microsoft, bundle_overrides_map, bundles_with_status_items, category_list,
|
||||||
category_list, changelog_error, changelog_groups, changelog_is_loading, cluster_content_items,
|
changelog_error, changelog_groups, changelog_is_loading, cluster_content_items,
|
||||||
content_type_for_slug, has_migration_data, invalidate_cluster_content_queries,
|
content_type_for_slug, has_migration_data, invalidate_cluster_content_queries,
|
||||||
invalidate_cluster_queries, invalidate_java_queries,
|
invalidate_cluster_queries, invalidate_java_queries, invalidate_leftovers_queries,
|
||||||
invalidate_logs_queries, invalidate_profile_queries, invalidate_screenshots_queries,
|
invalidate_logs_queries, invalidate_profile_queries, invalidate_screenshots_queries,
|
||||||
invalidate_storage_queries, try_storage_report, use_storage_action, use_storage_report,
|
invalidate_storage_queries, java_runtimes, latest_changelog_version, loaded_image,
|
||||||
DiscardLeftoversKeys, DiscardLeftoversMutation, LeftoversQuery,
|
loader_versions, login_code_already_handled, migration_detection, mutation_error,
|
||||||
UseDiscardLeftovers, invalidate_leftovers_queries, mutation_ok, try_leftovers,
|
mutation_is_pending, mutation_is_running, mutation_ok, onboarding_bundles_items,
|
||||||
use_discard_leftovers, use_leftovers,
|
package_meta_batch, package_updates, pick_version_metadata, project_detail, provider_versions,
|
||||||
java_runtimes, latest_changelog_version, loaded_image, loader_versions,
|
query_error, query_is_busy, query_is_loading, reset_login_code_dedup, search_items,
|
||||||
login_code_already_handled, migration_detection,
|
search_pending, search_total, settled_or_loading, stale_hashes, terms_document, terms_error,
|
||||||
mutation_error, mutation_is_pending, mutation_is_running, onboarding_bundles_items, package_meta_batch,
|
terms_is_loading, try_account, try_accounts, try_cluster_analytics, try_cluster_logs,
|
||||||
package_updates, pick_version_metadata, project_detail, provider_versions, query_error,
|
try_cluster_screenshots, try_default_account, try_game_profile, try_global_analytics,
|
||||||
query_is_busy, stale_hashes, use_package_updates,
|
try_leftovers, try_log_content, try_storage_report, use_account, use_accounts,
|
||||||
query_is_loading, reset_login_code_dedup, search_items, search_pending, search_total,
|
use_add_microsoft_account, use_add_offline_account, use_begin_microsoft_login,
|
||||||
settled_or_loading, terms_document, terms_error, terms_is_loading, try_account,
|
|
||||||
try_accounts, try_cluster_analytics, try_cluster_logs, try_cluster_screenshots,
|
|
||||||
try_default_account, try_game_profile, try_global_analytics, try_log_content, use_account,
|
|
||||||
use_accounts, use_add_microsoft_account, use_add_offline_account, use_begin_microsoft_login,
|
|
||||||
use_bundle_overrides, use_bundle_updates, use_bundles_with_status, use_cached_image,
|
use_bundle_overrides, use_bundle_updates, use_bundles_with_status, use_cached_image,
|
||||||
use_cancel_microsoft_login, use_changelog, use_cluster_analytics, use_cluster_content,
|
use_cancel_microsoft_login, use_changelog, use_cluster, use_cluster_analytics,
|
||||||
use_cluster_logs, use_cluster_mutation, use_cluster_profile, use_cluster_screenshots,
|
use_cluster_content, use_cluster_logs, use_cluster_mutation, use_cluster_profile,
|
||||||
use_cluster, use_cluster_settings, use_clusters, use_current_account, use_default_account,
|
use_cluster_screenshots, use_cluster_settings, use_clusters, use_current_account,
|
||||||
use_finish_microsoft_login, use_game_profile, use_global_analytics, use_java_runtimes,
|
use_default_account, use_discard_leftovers, use_finish_microsoft_login, use_game_profile,
|
||||||
use_loader_versions, use_local_image, use_log_action, use_log_content, use_migration,
|
use_global_analytics, use_java_runtimes, use_leftovers, use_loader_versions, use_local_image,
|
||||||
use_named_profiles, use_onboarding_bundles, use_package_categories, use_package_meta_batch,
|
use_log_action, use_log_content, use_migration, use_named_profiles, use_onboarding_bundles,
|
||||||
use_package_project, use_package_search, use_package_versions, use_package_versions_when,
|
use_package_categories, use_package_meta_batch, use_package_project, use_package_search,
|
||||||
use_player_profile,
|
use_package_updates, use_package_versions, use_package_versions_when, use_player_profile,
|
||||||
use_player_skin, use_provider_versions, use_refresh_account, use_refresh_all_accounts,
|
use_player_skin, use_provider_versions, use_refresh_account, use_refresh_all_accounts,
|
||||||
use_remove_account, use_screenshot_action, use_screenshot_folder_watch, use_set_default_account,
|
use_remove_account, use_screenshot_action, use_screenshot_folder_watch,
|
||||||
use_terms, use_upload_log,
|
use_set_default_account, use_storage_action, use_storage_report, use_terms, use_upload_log,
|
||||||
use_version_metadata, use_versions, version_list, versions_metadata, versions_total,
|
use_version_metadata, use_versions, version_list, versions_metadata, versions_total,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -117,7 +113,9 @@ pub fn use_account_switcher_open() -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_control_center_open() -> bool {
|
pub fn use_control_center_open() -> bool {
|
||||||
use_radio(AppChannel::ControlCenter).read().control_center_open
|
use_radio(AppChannel::ControlCenter)
|
||||||
|
.read()
|
||||||
|
.control_center_open
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_game_snapshot() -> GameState {
|
pub fn use_game_snapshot() -> GameState {
|
||||||
|
|
@ -148,4 +146,3 @@ pub fn use_microsoft_login_status() -> Option<LoginProgress> {
|
||||||
.microsoft_login
|
.microsoft_login
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,10 @@ impl OverlayClaims {
|
||||||
|
|
||||||
fn release(&self) {
|
fn release(&self) {
|
||||||
let held = self.0.get();
|
let held = self.0.get();
|
||||||
debug_assert!(held > 0, "overlay claim released without a matching acquire");
|
debug_assert!(
|
||||||
|
held > 0,
|
||||||
|
"overlay claim released without a matching acquire"
|
||||||
|
);
|
||||||
self.0.set(held.saturating_sub(1));
|
self.0.set(held.saturating_sub(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use freya::query::{
|
use freya::query::{
|
||||||
Mutation, MutationCapability, MutationStateData, QueriesStorage, Query, QueryCapability, UseMutation, UseQuery, use_mutation, use_query,
|
Mutation, MutationCapability, MutationStateData, QueriesStorage, Query, QueryCapability,
|
||||||
|
UseMutation, UseQuery, use_mutation, use_query,
|
||||||
};
|
};
|
||||||
use oneclient_core::LauncherError;
|
|
||||||
use oneclient_auth::{AccountKind, MicrosoftLoginSession, MinecraftAccount};
|
use oneclient_auth::{AccountKind, MicrosoftLoginSession, MinecraftAccount};
|
||||||
|
use oneclient_core::LauncherError;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
static HANDLED_LOGIN_CODE: Mutex<Option<String>> = Mutex::new(None);
|
static HANDLED_LOGIN_CODE: Mutex<Option<String>> = Mutex::new(None);
|
||||||
|
|
@ -132,7 +133,10 @@ impl MutationCapability for BeginMicrosoftLoginMutation {
|
||||||
type Keys = ();
|
type Keys = ();
|
||||||
|
|
||||||
async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
Ok(crate::launcher::state()?.auth.begin_microsoft_login().await?)
|
Ok(crate::launcher::state()?
|
||||||
|
.auth
|
||||||
|
.begin_microsoft_login()
|
||||||
|
.await?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,7 +149,10 @@ impl MutationCapability for FinishMicrosoftLoginMutation {
|
||||||
type Keys = MicrosoftLoginSession;
|
type Keys = MicrosoftLoginSession;
|
||||||
|
|
||||||
async fn run(&self, flow: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, flow: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
Ok(crate::launcher::state()?.auth.finish_microsoft_login(flow.clone()).await?)
|
Ok(crate::launcher::state()?
|
||||||
|
.auth
|
||||||
|
.finish_microsoft_login(flow.clone())
|
||||||
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_settled(&self, _keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
|
async fn on_settled(&self, _keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
|
||||||
|
|
@ -170,7 +177,10 @@ impl MutationCapability for CancelMicrosoftLoginMutation {
|
||||||
type Keys = CancelMicrosoftLoginKeys;
|
type Keys = CancelMicrosoftLoginKeys;
|
||||||
|
|
||||||
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
crate::launcher::state()?.auth.cancel_microsoft_login(&keys.state_token).await;
|
crate::launcher::state()?
|
||||||
|
.auth
|
||||||
|
.cancel_microsoft_login(&keys.state_token)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -210,7 +220,10 @@ impl MutationCapability for AddOfflineAccountMutation {
|
||||||
type Keys = AddOfflineAccountKeys;
|
type Keys = AddOfflineAccountKeys;
|
||||||
|
|
||||||
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
Ok(crate::launcher::state()?.auth.add_offline_account(keys.username.clone()).await?)
|
Ok(crate::launcher::state()?
|
||||||
|
.auth
|
||||||
|
.add_offline_account(keys.username.clone())
|
||||||
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_settled(&self, _keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
|
async fn on_settled(&self, _keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
|
||||||
|
|
@ -234,7 +247,10 @@ impl MutationCapability for RemoveAccountMutation {
|
||||||
type Keys = RemoveAccountKeys;
|
type Keys = RemoveAccountKeys;
|
||||||
|
|
||||||
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
Ok(crate::launcher::state()?.auth.remove_account(keys.id).await?)
|
Ok(crate::launcher::state()?
|
||||||
|
.auth
|
||||||
|
.remove_account(keys.id)
|
||||||
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_settled(&self, keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
|
async fn on_settled(&self, keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
|
||||||
|
|
@ -258,7 +274,10 @@ impl MutationCapability for SetDefaultAccountMutation {
|
||||||
type Keys = SetDefaultAccountKeys;
|
type Keys = SetDefaultAccountKeys;
|
||||||
|
|
||||||
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
Ok(crate::launcher::state()?.auth.set_default_account(keys.id).await?)
|
Ok(crate::launcher::state()?
|
||||||
|
.auth
|
||||||
|
.set_default_account(keys.id)
|
||||||
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_settled(&self, keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
|
async fn on_settled(&self, keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
|
||||||
|
|
@ -282,7 +301,10 @@ impl MutationCapability for RefreshAccountMutation {
|
||||||
type Keys = RefreshAccountKeys;
|
type Keys = RefreshAccountKeys;
|
||||||
|
|
||||||
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
Ok(crate::launcher::state()?.auth.refresh_account(keys.id).await?)
|
Ok(crate::launcher::state()?
|
||||||
|
.auth
|
||||||
|
.refresh_account(keys.id)
|
||||||
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_settled(&self, keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
|
async fn on_settled(&self, keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
|
||||||
|
|
@ -301,7 +323,10 @@ impl MutationCapability for RefreshAllAccountsMutation {
|
||||||
type Keys = ();
|
type Keys = ();
|
||||||
|
|
||||||
async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
Ok(crate::launcher::state()?.auth.refresh_all_accounts().await?)
|
Ok(crate::launcher::state()?
|
||||||
|
.auth
|
||||||
|
.refresh_all_accounts()
|
||||||
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_settled(&self, _keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
|
async fn on_settled(&self, _keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,11 @@ impl QueryCapability for OnboardingBundlesQuery {
|
||||||
for cluster in clusters {
|
for cluster in clusters {
|
||||||
let archives = state
|
let archives = state
|
||||||
.bundles
|
.bundles
|
||||||
.archives_for(&state.services.content(), &cluster.mc_version, cluster.mc_loader)
|
.archives_for(
|
||||||
|
&state.services.content(),
|
||||||
|
&cluster.mc_version,
|
||||||
|
cluster.mc_loader,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
out.push(ClusterBundles { cluster, archives });
|
out.push(ClusterBundles { cluster, archives });
|
||||||
|
|
@ -70,14 +74,12 @@ impl QueryCapability for BundlesWithStatusQuery {
|
||||||
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
let _ = keys;
|
let _ = keys;
|
||||||
let state = crate::launcher::state()?;
|
let state = crate::launcher::state()?;
|
||||||
Ok(
|
Ok(get_bundles_with_update_status(
|
||||||
get_bundles_with_update_status(
|
self.cluster_id,
|
||||||
self.cluster_id,
|
state.bundles.as_ref(),
|
||||||
state.bundles.as_ref(),
|
&state.services.content(),
|
||||||
&state.services.content(),
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
)
|
)
|
||||||
|
.await?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,7 +114,8 @@ impl QueryCapability for BundleOverridesQuery {
|
||||||
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
let _ = keys;
|
let _ = keys;
|
||||||
let state = crate::launcher::state()?;
|
let state = crate::launcher::state()?;
|
||||||
let rows = list_cluster_bundle_overrides(self.cluster_id, &state.services.content()).await?;
|
let rows =
|
||||||
|
list_cluster_bundle_overrides(self.cluster_id, &state.services.content()).await?;
|
||||||
Ok(rows
|
Ok(rows
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(bundle, pkg, ty)| ((bundle, pkg), ty))
|
.map(|(bundle, pkg, ty)| ((bundle, pkg), ty))
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
use freya::query::{Query, QueryCapability, UseQuery, use_query};
|
use freya::query::{Query, QueryCapability, UseQuery, use_query};
|
||||||
use oneclient_core::{
|
use oneclient_core::{ChangelogGroup, LauncherError, fetch_changelog, parse_changelog};
|
||||||
ChangelogGroup, LauncherError, fetch_changelog, parse_changelog,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct ChangelogKeys {
|
pub struct ChangelogKeys {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,8 @@ impl QueryCapability for ClusterContentQuery {
|
||||||
|
|
||||||
async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
let state = crate::launcher::state()?;
|
let state = crate::launcher::state()?;
|
||||||
let all = PackageStore::list_linked_artifacts(self.cluster_id, &state.services.content()).await?;
|
let all =
|
||||||
|
PackageStore::list_linked_artifacts(self.cluster_id, &state.services.content()).await?;
|
||||||
Ok(all
|
Ok(all
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|item| item.content_type == self.content_type)
|
.filter(|item| item.content_type == self.content_type)
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,10 @@ impl QueryCapability for CachedImageQuery {
|
||||||
|
|
||||||
/// Returns the url alongside the bytes callers key their image handle on it
|
/// Returns the url alongside the bytes callers key their image handle on it
|
||||||
/// and a stale url with fresh bytes renders the wrong picture for a frame
|
/// and a stale url with fresh bytes renders the wrong picture for a frame
|
||||||
pub fn loaded_image(url: Option<&str>, query: &UseQuery<CachedImageQuery>) -> Option<(String, Bytes)> {
|
pub fn loaded_image(
|
||||||
|
url: Option<&str>,
|
||||||
|
query: &UseQuery<CachedImageQuery>,
|
||||||
|
) -> Option<(String, Bytes)> {
|
||||||
let url = url?;
|
let url = url?;
|
||||||
let bytes = super::state::settled_or_loading(query)?;
|
let bytes = super::state::settled_or_loading(query)?;
|
||||||
Some((url.to_string(), bytes))
|
Some((url.to_string(), bytes))
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,7 @@ impl QueryCapability for ListJavaRuntimesQuery {
|
||||||
|
|
||||||
async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
let state = crate::launcher::state().map_err(|e| e.to_string())?;
|
let state = crate::launcher::state().map_err(|e| e.to_string())?;
|
||||||
state.java.list_runtimes()
|
state.java.list_runtimes().await.map_err(|e| e.to_string())
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,7 +45,9 @@ impl QueryCapability for ProviderVersionsQuery {
|
||||||
|
|
||||||
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
let state = crate::launcher::state().map_err(|e| e.to_string())?;
|
let state = crate::launcher::state().map_err(|e| e.to_string())?;
|
||||||
state.java.available_versions(&keys.vendor)
|
state
|
||||||
|
.java
|
||||||
|
.available_versions(&keys.vendor)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use freya::query::{
|
use freya::query::{
|
||||||
Mutation, MutationCapability, QueriesStorage, Query, QueryCapability,
|
Mutation, MutationCapability, QueriesStorage, Query, QueryCapability, UseMutation, UseQuery,
|
||||||
UseMutation, UseQuery, use_mutation, use_query,
|
use_mutation, use_query,
|
||||||
};
|
};
|
||||||
use oneclient_core::{
|
use oneclient_core::{
|
||||||
LauncherError, LogFileInfo, LogLevel, LogLine, MclogsUploadResponse, ReadOptions,
|
LauncherError, LogFileInfo, LogLevel, LogLine, MclogsUploadResponse, ReadOptions,
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,11 @@ pub use auth::{
|
||||||
CancelMicrosoftLoginMutation, FinishMicrosoftLoginMutation, RefreshAccountKeys,
|
CancelMicrosoftLoginMutation, FinishMicrosoftLoginMutation, RefreshAccountKeys,
|
||||||
RemoveAccountKeys, SetDefaultAccountKeys, UseRefreshAccount, UseRemoveAccount,
|
RemoveAccountKeys, SetDefaultAccountKeys, UseRefreshAccount, UseRemoveAccount,
|
||||||
UseSetDefaultAccount, accounts_have_microsoft, login_code_already_handled, mutation_error,
|
UseSetDefaultAccount, accounts_have_microsoft, login_code_already_handled, mutation_error,
|
||||||
mutation_is_pending, mutation_is_running, mutation_ok, reset_login_code_dedup, try_account, try_accounts, try_default_account,
|
mutation_is_pending, mutation_is_running, mutation_ok, reset_login_code_dedup, try_account,
|
||||||
use_account, use_accounts, use_add_microsoft_account, use_add_offline_account,
|
try_accounts, try_default_account, use_account, use_accounts, use_add_microsoft_account,
|
||||||
use_begin_microsoft_login, use_cancel_microsoft_login, use_current_account,
|
use_add_offline_account, use_begin_microsoft_login, use_cancel_microsoft_login,
|
||||||
use_default_account, use_finish_microsoft_login, use_refresh_account, use_refresh_all_accounts,
|
use_current_account, use_default_account, use_finish_microsoft_login, use_refresh_account,
|
||||||
use_remove_account, use_set_default_account,
|
use_refresh_all_accounts, use_remove_account, use_set_default_account,
|
||||||
};
|
};
|
||||||
pub use bundles::{
|
pub use bundles::{
|
||||||
ClusterBundles, OnboardingBundlesQuery, bundle_overrides_map, bundles_with_status_items,
|
ClusterBundles, OnboardingBundlesQuery, bundle_overrides_map, bundles_with_status_items,
|
||||||
|
|
@ -52,7 +52,8 @@ pub use bundles::{
|
||||||
use_onboarding_bundles,
|
use_onboarding_bundles,
|
||||||
};
|
};
|
||||||
pub use changelog::{
|
pub use changelog::{
|
||||||
changelog_error, changelog_groups, changelog_is_loading, latest_changelog_version, use_changelog,
|
changelog_error, changelog_groups, changelog_is_loading, latest_changelog_version,
|
||||||
|
use_changelog,
|
||||||
};
|
};
|
||||||
pub use cluster_content::{cluster_content_items, use_cluster_content};
|
pub use cluster_content::{cluster_content_items, use_cluster_content};
|
||||||
pub use clusters::{use_cluster, use_clusters};
|
pub use clusters::{use_cluster, use_clusters};
|
||||||
|
|
@ -67,13 +68,6 @@ pub use logs::{
|
||||||
use_log_action, use_log_content, use_upload_log,
|
use_log_action, use_log_content, use_upload_log,
|
||||||
};
|
};
|
||||||
pub use migration::{MigrationQuery, has_migration_data, migration_detection, use_migration};
|
pub use migration::{MigrationQuery, has_migration_data, migration_detection, use_migration};
|
||||||
pub use storage::{
|
|
||||||
DiscardLeftoversKeys, DiscardLeftoversMutation, LeftoversQuery,
|
|
||||||
StorageAction, StorageActionMutation, StorageReportQuery, UseDiscardLeftovers,
|
|
||||||
UseStorageAction, invalidate_leftovers_queries, invalidate_storage_queries, try_leftovers,
|
|
||||||
try_storage_report, use_discard_leftovers, use_leftovers, use_storage_action,
|
|
||||||
use_storage_report,
|
|
||||||
};
|
|
||||||
pub use mutations::{
|
pub use mutations::{
|
||||||
ClusterAction, invalidate_cluster_content_queries, invalidate_cluster_queries,
|
ClusterAction, invalidate_cluster_content_queries, invalidate_cluster_queries,
|
||||||
invalidate_profile_queries, use_cluster_mutation,
|
invalidate_profile_queries, use_cluster_mutation,
|
||||||
|
|
@ -83,8 +77,7 @@ pub use packages::{
|
||||||
BROWSE_PAGE_SIZE, VERSIONS_PAGE_SIZE, category_list, content_type_for_slug, package_meta_batch,
|
BROWSE_PAGE_SIZE, VERSIONS_PAGE_SIZE, category_list, content_type_for_slug, package_meta_batch,
|
||||||
project_detail, search_items, search_pending, search_total, use_package_categories,
|
project_detail, search_items, search_pending, search_total, use_package_categories,
|
||||||
use_package_meta_batch, use_package_project, use_package_search, use_package_versions,
|
use_package_meta_batch, use_package_project, use_package_search, use_package_versions,
|
||||||
use_package_versions_when,
|
use_package_versions_when, version_list, versions_total,
|
||||||
version_list, versions_total,
|
|
||||||
};
|
};
|
||||||
pub use player_profile::use_player_profile;
|
pub use player_profile::use_player_profile;
|
||||||
pub use screenshots::{
|
pub use screenshots::{
|
||||||
|
|
@ -97,6 +90,12 @@ pub use settings_profiles::{
|
||||||
};
|
};
|
||||||
pub use skin::use_player_skin;
|
pub use skin::use_player_skin;
|
||||||
pub use state::{query_error, query_is_busy, query_is_loading, settled_or_loading};
|
pub use state::{query_error, query_is_busy, query_is_loading, settled_or_loading};
|
||||||
|
pub use storage::{
|
||||||
|
DiscardLeftoversKeys, DiscardLeftoversMutation, LeftoversQuery, StorageAction,
|
||||||
|
StorageActionMutation, StorageReportQuery, UseDiscardLeftovers, UseStorageAction,
|
||||||
|
invalidate_leftovers_queries, invalidate_storage_queries, try_leftovers, try_storage_report,
|
||||||
|
use_discard_leftovers, use_leftovers, use_storage_action, use_storage_report,
|
||||||
|
};
|
||||||
pub use tos::{TermsQuery, terms_document, terms_error, terms_is_loading, use_terms};
|
pub use tos::{TermsQuery, terms_document, terms_error, terms_is_loading, use_terms};
|
||||||
pub use version_metadata::{pick_version_metadata, use_version_metadata};
|
pub use version_metadata::{pick_version_metadata, use_version_metadata};
|
||||||
pub use versions::{loader_versions, use_loader_versions, use_versions, versions_metadata};
|
pub use versions::{loader_versions, use_loader_versions, use_versions, versions_metadata};
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,36 @@ async fn timed(step: &'static str, fut: impl std::future::Future<Output = ()>) {
|
||||||
|
|
||||||
pub async fn invalidate_cluster_queries() {
|
pub async fn invalidate_cluster_queries() {
|
||||||
let started = std::time::Instant::now();
|
let started = std::time::Instant::now();
|
||||||
timed("cluster_content", QueriesStorage::<ClusterContentQuery>::invalidate_all()).await;
|
timed(
|
||||||
timed("bundle_overrides", QueriesStorage::<BundleOverridesQuery>::invalidate_all()).await;
|
"cluster_content",
|
||||||
timed("bundles_with_status", QueriesStorage::<BundlesWithStatusQuery>::invalidate_all()).await;
|
QueriesStorage::<ClusterContentQuery>::invalidate_all(),
|
||||||
timed("clusters", QueriesStorage::<ListClustersQuery>::invalidate_all()).await;
|
)
|
||||||
timed("bundle_updates", QueriesStorage::<BundleUpdatesQuery>::invalidate_all()).await;
|
.await;
|
||||||
timed("package_updates", QueriesStorage::<PackageUpdatesQuery>::invalidate_all()).await;
|
timed(
|
||||||
|
"bundle_overrides",
|
||||||
|
QueriesStorage::<BundleOverridesQuery>::invalidate_all(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
timed(
|
||||||
|
"bundles_with_status",
|
||||||
|
QueriesStorage::<BundlesWithStatusQuery>::invalidate_all(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
timed(
|
||||||
|
"clusters",
|
||||||
|
QueriesStorage::<ListClustersQuery>::invalidate_all(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
timed(
|
||||||
|
"bundle_updates",
|
||||||
|
QueriesStorage::<BundleUpdatesQuery>::invalidate_all(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
timed(
|
||||||
|
"package_updates",
|
||||||
|
QueriesStorage::<PackageUpdatesQuery>::invalidate_all(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
target: "oneclient_app::perf",
|
target: "oneclient_app::perf",
|
||||||
ms = started.elapsed().as_millis() as u64,
|
ms = started.elapsed().as_millis() as u64,
|
||||||
|
|
@ -47,10 +71,26 @@ pub async fn invalidate_cluster_content_queries() {
|
||||||
/// The bundle queries do move a toggle writes a bundle override and both read
|
/// The bundle queries do move a toggle writes a bundle override and both read
|
||||||
/// those back
|
/// those back
|
||||||
async fn invalidate_enabled_flag_queries() {
|
async fn invalidate_enabled_flag_queries() {
|
||||||
timed("cluster_content", QueriesStorage::<ClusterContentQuery>::invalidate_all()).await;
|
timed(
|
||||||
timed("bundle_overrides", QueriesStorage::<BundleOverridesQuery>::invalidate_all()).await;
|
"cluster_content",
|
||||||
timed("bundles_with_status", QueriesStorage::<BundlesWithStatusQuery>::invalidate_all()).await;
|
QueriesStorage::<ClusterContentQuery>::invalidate_all(),
|
||||||
timed("bundle_updates", QueriesStorage::<BundleUpdatesQuery>::invalidate_all()).await;
|
)
|
||||||
|
.await;
|
||||||
|
timed(
|
||||||
|
"bundle_overrides",
|
||||||
|
QueriesStorage::<BundleOverridesQuery>::invalidate_all(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
timed(
|
||||||
|
"bundles_with_status",
|
||||||
|
QueriesStorage::<BundlesWithStatusQuery>::invalidate_all(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
timed(
|
||||||
|
"bundle_updates",
|
||||||
|
QueriesStorage::<BundleUpdatesQuery>::invalidate_all(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn invalidate_profile_queries() {
|
pub async fn invalidate_profile_queries() {
|
||||||
|
|
@ -195,7 +235,13 @@ impl MutationCapability for ClusterMutation {
|
||||||
if let Err(err) = result
|
if let Err(err) = result
|
||||||
&& let Ok(state) = crate::launcher::state()
|
&& let Ok(state) = crate::launcher::state()
|
||||||
{
|
{
|
||||||
state.services.events.notify("Action failed").body(err).error().send();
|
state
|
||||||
|
.services
|
||||||
|
.events
|
||||||
|
.notify("Action failed")
|
||||||
|
.body(err)
|
||||||
|
.error()
|
||||||
|
.send();
|
||||||
}
|
}
|
||||||
if matches!(keys, ClusterAction::SetArtifactEnabled { .. }) {
|
if matches!(keys, ClusterAction::SetArtifactEnabled { .. }) {
|
||||||
invalidate_enabled_flag_queries().await;
|
invalidate_enabled_flag_queries().await;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,10 @@ impl QueryCapability for FetchPlayerProfileQuery {
|
||||||
let state = crate::launcher::state()?;
|
let state = crate::launcher::state()?;
|
||||||
let client = &state.services.requester;
|
let client = &state.services.requester;
|
||||||
|
|
||||||
Ok(minecraft::fetch_player_profile_view(client, &keys.uuid, access_token.as_deref()).await?)
|
Ok(
|
||||||
|
minecraft::fetch_player_profile_view(client, &keys.uuid, access_token.as_deref())
|
||||||
|
.await?,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Cache
|
// TODO Cache
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ use std::time::Duration;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use freya::prelude::{spawn, use_hook};
|
use freya::prelude::{spawn, use_hook};
|
||||||
use freya::query::{
|
use freya::query::{
|
||||||
Mutation, MutationCapability, QueriesStorage, Query, QueryCapability,
|
Mutation, MutationCapability, QueriesStorage, Query, QueryCapability, UseMutation, UseQuery,
|
||||||
UseMutation, UseQuery, use_mutation, use_query,
|
use_mutation, use_query,
|
||||||
};
|
};
|
||||||
use notify::{EventKind, RecursiveMode, Watcher};
|
use notify::{EventKind, RecursiveMode, Watcher};
|
||||||
use oneclient_core::{LauncherError, ScreenshotInfo};
|
use oneclient_core::{LauncherError, ScreenshotInfo};
|
||||||
|
|
@ -119,15 +119,14 @@ async fn watch_folder(
|
||||||
std::fs::create_dir_all(folder)?;
|
std::fs::create_dir_all(folder)?;
|
||||||
|
|
||||||
let (tx, mut rx) = mpsc::unbounded_channel();
|
let (tx, mut rx) = mpsc::unbounded_channel();
|
||||||
let mut watcher = notify::recommended_watcher(move |event: notify::Result<notify::Event>| {
|
let mut watcher =
|
||||||
match event {
|
notify::recommended_watcher(move |event: notify::Result<notify::Event>| match event {
|
||||||
Ok(event) if touches_image(&event) => {
|
Ok(event) if touches_image(&event) => {
|
||||||
let _ = tx.send(());
|
let _ = tx.send(());
|
||||||
}
|
}
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => tracing::debug!(error = %err, "screenshot watcher reported an error"),
|
Err(err) => tracing::debug!(error = %err, "screenshot watcher reported an error"),
|
||||||
}
|
})?;
|
||||||
})?;
|
|
||||||
watcher.watch(folder, RecursiveMode::NonRecursive)?;
|
watcher.watch(folder, RecursiveMode::NonRecursive)?;
|
||||||
|
|
||||||
let mut pending = false;
|
let mut pending = false;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use freya::query::{Query, QueryCapability, UseQuery, use_query};
|
use freya::query::{Query, QueryCapability, UseQuery, use_query};
|
||||||
use oneclient_core::settings::GameSettingsProfile;
|
|
||||||
use oneclient_cluster::profiles::{
|
use oneclient_cluster::profiles::{
|
||||||
get_profile_or_default, list_named_profiles, resolve_cluster_profile,
|
get_profile_or_default, list_named_profiles, resolve_cluster_profile,
|
||||||
};
|
};
|
||||||
|
use oneclient_core::settings::GameSettingsProfile;
|
||||||
use oneclient_db::models::ClusterId;
|
use oneclient_db::models::ClusterId;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
|
@ -110,11 +110,15 @@ impl QueryCapability for ClusterSettingsQuery {
|
||||||
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
let cluster_id = keys.cluster_id;
|
let cluster_id = keys.cluster_id;
|
||||||
let state = crate::launcher::state().map_err(|e| e.to_string())?;
|
let state = crate::launcher::state().map_err(|e| e.to_string())?;
|
||||||
let cluster = state.clusters.get(cluster_id)
|
let cluster = state
|
||||||
|
.clusters
|
||||||
|
.get(cluster_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
let global = state.settings.read().global_game_settings.clone();
|
let global = state.settings.read().global_game_settings.clone();
|
||||||
state.clusters.resolve_settings(&global, &cluster)
|
state
|
||||||
|
.clusters
|
||||||
|
.resolve_settings(&global, &cluster)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use oneclient_common::VersionKey;
|
use oneclient_common::VersionKey;
|
||||||
use oneclient_core::VersionMetadata;
|
|
||||||
use oneclient_common::domain::GameLoader;
|
use oneclient_common::domain::GameLoader;
|
||||||
|
use oneclient_core::VersionMetadata;
|
||||||
|
|
||||||
use super::use_versions;
|
use super::use_versions;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,18 @@ impl QueryCapability for VersionsMetadataQuery {
|
||||||
|
|
||||||
async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
|
||||||
let state = crate::launcher::state()?;
|
let state = crate::launcher::state()?;
|
||||||
let metadata = state.versions.metadata(&state.services.requester.config().meta_url_base).await;
|
let metadata = state
|
||||||
|
.versions
|
||||||
|
.metadata(&state.services.requester.config().meta_url_base)
|
||||||
|
.await;
|
||||||
if !metadata.is_empty() {
|
if !metadata.is_empty() {
|
||||||
return Ok(metadata);
|
return Ok(metadata);
|
||||||
}
|
}
|
||||||
state.versions.sync(&state.services).await?;
|
state.versions.sync(&state.services).await?;
|
||||||
Ok(state.versions.metadata(&state.services.requester.config().meta_url_base).await)
|
Ok(state
|
||||||
|
.versions
|
||||||
|
.metadata(&state.services.requester.config().meta_url_base)
|
||||||
|
.await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,10 @@ mod imp {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn bind() -> Result<Listener, BindError> {
|
pub async fn bind() -> Result<Listener, BindError> {
|
||||||
match ServerOptions::new().first_pipe_instance(true).create(ENDPOINT) {
|
match ServerOptions::new()
|
||||||
|
.first_pipe_instance(true)
|
||||||
|
.create(ENDPOINT)
|
||||||
|
{
|
||||||
Ok(server) => Ok(Listener { server }),
|
Ok(server) => Ok(Listener { server }),
|
||||||
Err(err) if err.raw_os_error() == Some(ERROR_ACCESS_DENIED) => Err(BindError::Taken),
|
Err(err) if err.raw_os_error() == Some(ERROR_ACCESS_DENIED) => Err(BindError::Taken),
|
||||||
Err(err) => Err(BindError::Io(err)),
|
Err(err) => Err(BindError::Io(err)),
|
||||||
|
|
@ -244,7 +247,10 @@ mod imp {
|
||||||
let listener = UnixListener::bind(path)?;
|
let listener = UnixListener::bind(path)?;
|
||||||
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600))?;
|
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600))?;
|
||||||
|
|
||||||
Ok(Listener { listener, path: path.to_path_buf() })
|
Ok(Listener {
|
||||||
|
listener,
|
||||||
|
path: path.to_path_buf(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(request: &str) -> bool {
|
pub async fn send(request: &str) -> bool {
|
||||||
|
|
@ -284,7 +290,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn focus_survives_the_wire() {
|
fn focus_survives_the_wire() {
|
||||||
assert_eq!(IpcCommand::decode(&IpcCommand::Focus.encode()), Some(IpcCommand::Focus));
|
assert_eq!(
|
||||||
|
IpcCommand::decode(&IpcCommand::Focus.encode()),
|
||||||
|
Some(IpcCommand::Focus)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -313,7 +322,9 @@ mod tests {
|
||||||
fn a_bare_start_asks_only_for_the_window() {
|
fn a_bare_start_asks_only_for_the_window() {
|
||||||
assert_eq!(request_for(&Cli::default()), IpcCommand::Focus);
|
assert_eq!(request_for(&Cli::default()), IpcCommand::Focus);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
request_for(&Cli { launch: Some("pack".into()) }),
|
request_for(&Cli {
|
||||||
|
launch: Some("pack".into())
|
||||||
|
}),
|
||||||
IpcCommand::Launch("pack".into()),
|
IpcCommand::Launch("pack".into()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,5 @@ pub fn install(state: Arc<LauncherState>) -> Arc<LauncherState> {
|
||||||
|
|
||||||
/// Errors with [`LauncherError::NotInitialized`] before startup installs the handle
|
/// Errors with [`LauncherError::NotInitialized`] before startup installs the handle
|
||||||
pub fn state() -> LauncherResult<Arc<LauncherState>> {
|
pub fn state() -> LauncherResult<Arc<LauncherState>> {
|
||||||
LAUNCHER
|
LAUNCHER.get().cloned().ok_or(LauncherError::NotInitialized)
|
||||||
.get()
|
|
||||||
.cloned()
|
|
||||||
.ok_or(LauncherError::NotInitialized)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ impl Component for AppShell {
|
||||||
.on_global_file_hover_cancelled(move |_| drop_hovering.set(false))
|
.on_global_file_hover_cancelled(move |_| drop_hovering.set(false))
|
||||||
.on_file_drop(move |e: Event<FileEventData>| {
|
.on_file_drop(move |e: Event<FileEventData>| {
|
||||||
drop_hovering.set(false);
|
drop_hovering.set(false);
|
||||||
drop_pending.write().extend_from_slice(&e.file_paths);
|
drop_pending.write().extend_from_slice(&e.file_paths);
|
||||||
})
|
})
|
||||||
.child(AppNavbar)
|
.child(AppNavbar)
|
||||||
.child(AppHomeBackground)
|
.child(AppHomeBackground)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ use freya::router::*;
|
||||||
use oneclient_common::parse_mc_version;
|
use oneclient_common::parse_mc_version;
|
||||||
|
|
||||||
use crate::components::{Button, Icon, IconType, TabBar, TabItem};
|
use crate::components::{Button, Icon, IconType, TabBar, TabItem};
|
||||||
use crate::hooks::{use_cluster, use_dispatch, use_game_snapshot, use_launcher, use_version_metadata};
|
use crate::hooks::{
|
||||||
|
use_cluster, use_dispatch, use_game_snapshot, use_launcher, use_version_metadata,
|
||||||
|
};
|
||||||
use crate::routes::Route;
|
use crate::routes::Route;
|
||||||
use crate::theme::colors;
|
use crate::theme::colors;
|
||||||
use crate::ui::entrance_motion_layer;
|
use crate::ui::entrance_motion_layer;
|
||||||
|
|
@ -107,7 +109,9 @@ impl Component for ClusterShell {
|
||||||
|
|
||||||
// Queried unconditionally the shell can mount before the cluster list settles
|
// Queried unconditionally the shell can mount before the cluster list settles
|
||||||
// and a conditional hook would change this component's hook count mid-life
|
// and a conditional hook would change this component's hook count mid-life
|
||||||
let parsed = cluster.as_ref().and_then(|c| parse_mc_version(&c.mc_version));
|
let parsed = cluster
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|c| parse_mc_version(&c.mc_version));
|
||||||
let metadata = use_version_metadata(
|
let metadata = use_version_metadata(
|
||||||
parsed.as_ref().map(|p| p.major),
|
parsed.as_ref().map(|p| p.major),
|
||||||
parsed.and_then(|p| p.key()),
|
parsed.and_then(|p| p.key()),
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,10 @@ fn report_already_open(
|
||||||
(None, true) => format!("{} is open already.", cluster.name),
|
(None, true) => format!("{} is open already.", cluster.name),
|
||||||
(None, false) => format!("{} is on its way up.", cluster.name),
|
(None, false) => format!("{} is on its way up.", cluster.name),
|
||||||
(Some(other), true) => {
|
(Some(other), true) => {
|
||||||
format!("{other} is open. Close it before launching {}.", cluster.name)
|
format!(
|
||||||
|
"{other} is open. Close it before launching {}.",
|
||||||
|
cluster.name
|
||||||
|
)
|
||||||
}
|
}
|
||||||
(Some(other), false) => format!(
|
(Some(other), false) => format!(
|
||||||
"{other} is on its way up. Wait for it before launching {}.",
|
"{other} is on its way up. Wait for it before launching {}.",
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ use freya::prelude::*;
|
||||||
use freya::router::*;
|
use freya::router::*;
|
||||||
|
|
||||||
use crate::components::{
|
use crate::components::{
|
||||||
AccountSwitcher, ClusterUpdatePopup, ControlCenter, GenericPromptOverlay, JavaPromptOverlay, OptionalModsPopup, MicrosoftJavaPromptOverlay,
|
AccountSwitcher, ClusterUpdatePopup, ControlCenter, GenericPromptOverlay, JavaPromptOverlay,
|
||||||
NotificationCenter, PackageUpdatePopup, SplashCurtain, StatusBar, Toasts, TooltipHost,
|
MicrosoftJavaPromptOverlay, NotificationCenter, OptionalModsPopup, PackageUpdatePopup,
|
||||||
UpdatePromptOverlay, use_provide_tooltips,
|
SplashCurtain, StatusBar, Toasts, TooltipHost, UpdatePromptOverlay, use_provide_tooltips,
|
||||||
};
|
};
|
||||||
use crate::hooks::{SplashState, use_provide_overlay_claims, use_provide_splash};
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
use crate::hooks::use_start_maximized;
|
use crate::hooks::use_start_maximized;
|
||||||
|
use crate::hooks::{SplashState, use_provide_overlay_claims, use_provide_splash};
|
||||||
use crate::layout::{HomeArtPrefetch, PendingLaunchDriver};
|
use crate::layout::{HomeArtPrefetch, PendingLaunchDriver};
|
||||||
use crate::motion::AnimationClockDriver;
|
use crate::motion::AnimationClockDriver;
|
||||||
use crate::routes::Route;
|
use crate::routes::Route;
|
||||||
|
|
@ -26,45 +26,12 @@ impl Component for RootLayout {
|
||||||
use_provide_overlay_claims();
|
use_provide_overlay_claims();
|
||||||
use_provide_tooltips();
|
use_provide_tooltips();
|
||||||
|
|
||||||
// macOS rounds the window natively so Freya must not round on top of it
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
let corner = 0.;
|
|
||||||
|
|
||||||
// Elsewhere the window is borderless and squared when maximized
|
|
||||||
// No reactive maximized signal exists so root_size changes stand in as the trigger
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
let corner = {
|
|
||||||
let root_size = Platform::get().root_size;
|
|
||||||
// Seeded because the query below only answers after the window is visible
|
|
||||||
// which would round the corners of the first frames of a maximized launch
|
|
||||||
let start_maximized = use_start_maximized();
|
|
||||||
let mut maximized = use_state(move || start_maximized);
|
|
||||||
let size = *root_size.read();
|
|
||||||
let dep = (size.width as i32, size.height as i32);
|
|
||||||
use_side_effect_with_deps(&dep, move |_| {
|
|
||||||
spawn(async move {
|
|
||||||
let is_max = Platform::get()
|
|
||||||
.post_callback(|id, ctx| {
|
|
||||||
ctx.windows.get(&id).map(|w| w.window().is_maximized())
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
if let Ok(Some(is_max)) = is_max {
|
|
||||||
if *maximized.peek() != is_max {
|
|
||||||
maximized.set(is_max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if *maximized.read() { 0. } else { 12. }
|
|
||||||
};
|
|
||||||
|
|
||||||
rect()
|
rect()
|
||||||
.width(Size::fill())
|
.width(Size::fill())
|
||||||
.height(Size::fill())
|
.height(Size::fill())
|
||||||
.background(colors::page())
|
.background(colors::page())
|
||||||
.color(colors::fg_primary())
|
.color(colors::fg_primary())
|
||||||
.font_family(theme::DEFAULT_FONT)
|
.font_family(theme::DEFAULT_FONT)
|
||||||
//.corner_radius(CornerRadius::new_all(corner))
|
|
||||||
.overflow(Overflow::Clip)
|
.overflow(Overflow::Clip)
|
||||||
.child(
|
.child(
|
||||||
rect()
|
rect()
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,16 @@ const SEARCH_INDEX: &[SearchItem] = &[
|
||||||
title: "Launcher Folder",
|
title: "Launcher Folder",
|
||||||
description: "Open the launcher data directory, or move it to another drive.",
|
description: "Open the launcher data directory, or move it to another drive.",
|
||||||
keywords: &[
|
keywords: &[
|
||||||
"data dir", "directory", "folder", "open", "move", "change", "relocate", "location",
|
"data dir",
|
||||||
"drive", "disk",
|
"directory",
|
||||||
|
"folder",
|
||||||
|
"open",
|
||||||
|
"move",
|
||||||
|
"change",
|
||||||
|
"relocate",
|
||||||
|
"location",
|
||||||
|
"drive",
|
||||||
|
"disk",
|
||||||
],
|
],
|
||||||
route: Route::SettingsLauncher {},
|
route: Route::SettingsLauncher {},
|
||||||
},
|
},
|
||||||
|
|
@ -457,11 +465,7 @@ impl Component for SettingsShell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_header(
|
fn content_header(title: String, search: State<String>, mut width: State<f32>) -> impl IntoElement {
|
||||||
title: String,
|
|
||||||
search: State<String>,
|
|
||||||
mut width: State<f32>,
|
|
||||||
) -> impl IntoElement {
|
|
||||||
let search_width = search_width_for(*width.read());
|
let search_width = search_width_for(*width.read());
|
||||||
|
|
||||||
rect()
|
rect()
|
||||||
|
|
@ -770,9 +774,7 @@ impl Component for SidebarInfo {
|
||||||
.width(Size::fill())
|
.width(Size::fill())
|
||||||
.spacing(4.)
|
.spacing(4.)
|
||||||
.font_size(12.)
|
.font_size(12.)
|
||||||
.padding(Gaps::new(
|
.padding(Gaps::new(15., 0., 0., 0.))
|
||||||
15.,0.,0.,0.
|
|
||||||
))
|
|
||||||
.color(colors::fg_secondary())
|
.color(colors::fg_secondary())
|
||||||
.children(
|
.children(
|
||||||
items
|
items
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,12 @@
|
||||||
mod assets;
|
mod assets;
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
mod components;
|
mod components;
|
||||||
pub mod hooks;
|
|
||||||
pub mod events;
|
pub mod events;
|
||||||
pub(crate) mod file_content;
|
pub(crate) mod file_content;
|
||||||
|
pub mod hooks;
|
||||||
mod install;
|
mod install;
|
||||||
pub mod ipc;
|
pub mod ipc;
|
||||||
mod launcher;
|
mod launcher;
|
||||||
pub mod state;
|
|
||||||
mod transfer;
|
|
||||||
mod layout;
|
mod layout;
|
||||||
pub mod microsoft_java;
|
pub mod microsoft_java;
|
||||||
mod motion;
|
mod motion;
|
||||||
|
|
@ -23,7 +21,9 @@ pub mod protocol;
|
||||||
pub mod recovery;
|
pub mod recovery;
|
||||||
mod routes;
|
mod routes;
|
||||||
pub mod shortcut;
|
pub mod shortcut;
|
||||||
|
pub mod state;
|
||||||
pub mod theme;
|
pub mod theme;
|
||||||
|
mod transfer;
|
||||||
mod ui;
|
mod ui;
|
||||||
pub mod updater;
|
pub mod updater;
|
||||||
pub(crate) mod utils;
|
pub(crate) mod utils;
|
||||||
|
|
@ -32,8 +32,8 @@ mod view;
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
|
|
||||||
pub use assets::AppAssets;
|
pub use assets::AppAssets;
|
||||||
pub use events::EventPump;
|
|
||||||
pub use state::{AppChannel, AppState};
|
|
||||||
pub use components::ConfirmLinkOverlay;
|
pub use components::ConfirmLinkOverlay;
|
||||||
|
pub use events::EventPump;
|
||||||
pub use hooks::*;
|
pub use hooks::*;
|
||||||
pub use routes::{Route, router};
|
pub use routes::{Route, router};
|
||||||
|
pub use state::{AppChannel, AppState};
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ use oneclient_app::ipc::{self, Claim};
|
||||||
use oneclient_app::state::{AppChannel, AppState, LauncherInit};
|
use oneclient_app::state::{AppChannel, AppState, LauncherInit};
|
||||||
use oneclient_app::{
|
use oneclient_app::{
|
||||||
Actions, ConfirmLinkOverlay, EventPump, LinkConfirmState, StartMaximizedState, cli, constants,
|
Actions, ConfirmLinkOverlay, EventPump, LinkConfirmState, StartMaximizedState, cli, constants,
|
||||||
events, platform, router, theme, use_provide_actions, use_provide_link_confirm,
|
events, microsoft_java, platform, router, theme, use_provide_actions, use_provide_link_confirm,
|
||||||
use_provide_start_maximized, microsoft_java,
|
use_provide_start_maximized,
|
||||||
};
|
};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use tokio::runtime::Builder;
|
use tokio::runtime::Builder;
|
||||||
|
|
@ -196,39 +196,40 @@ fn main() {
|
||||||
boot_launch: Cell::new(cli.launch),
|
boot_launch: Cell::new(cli.launch),
|
||||||
ipc: Cell::new(ipc),
|
ipc: Cell::new(ipc),
|
||||||
})
|
})
|
||||||
.with_title(constants::WINDOW_TITLE)
|
.with_title(constants::WINDOW_TITLE)
|
||||||
.with_app_id(constants::WINDOW_APP_ID)
|
.with_app_id(constants::WINDOW_APP_ID)
|
||||||
.with_icon(LaunchConfig::window_icon(include_bytes!(
|
.with_icon(LaunchConfig::window_icon(include_bytes!(
|
||||||
"../icons/128x128.png"
|
"../icons/128x128.png"
|
||||||
)))
|
)))
|
||||||
.with_size(1200., 800.)
|
.with_size(1200., 800.)
|
||||||
.with_min_size(800., 600.)
|
.with_min_size(800., 600.)
|
||||||
.with_transparency(false)
|
.with_transparency(false)
|
||||||
.with_background(Color::TRANSPARENT)
|
.with_background(Color::TRANSPARENT)
|
||||||
// A half-copied data folder is unrecoverable, so the window refuses to
|
// A half-copied data folder is unrecoverable, so the window refuses to
|
||||||
// close while one is being moved; the move screen says as much
|
// close while one is being moved; the move screen says as much
|
||||||
.with_on_close(|_, _| {
|
.with_on_close(|_, _| {
|
||||||
if oneclient_core::relocate::in_progress() {
|
if oneclient_core::relocate::in_progress() {
|
||||||
tracing::warn!("close request ignored, the data folder is still being moved");
|
tracing::warn!("close request ignored, the data folder is still being moved");
|
||||||
CloseDecision::KeepOpen
|
CloseDecision::KeepOpen
|
||||||
} else {
|
} else {
|
||||||
CloseDecision::Close
|
CloseDecision::Close
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
let window_config = window_config
|
let window_config =
|
||||||
.with_decorations(true)
|
window_config
|
||||||
.with_window_attributes(move |attrs, _| {
|
.with_decorations(true)
|
||||||
use freya::winit::platform::macos::WindowAttributesExtMacOS;
|
.with_window_attributes(move |attrs, _| {
|
||||||
attrs
|
use freya::winit::platform::macos::WindowAttributesExtMacOS;
|
||||||
.with_title_hidden(true)
|
attrs
|
||||||
.with_titlebar_transparent(true)
|
.with_title_hidden(true)
|
||||||
.with_titlebar_buttons_hidden(true)
|
.with_titlebar_transparent(true)
|
||||||
.with_fullsize_content_view(true)
|
.with_titlebar_buttons_hidden(true)
|
||||||
.with_has_shadow(true)
|
.with_fullsize_content_view(true)
|
||||||
.with_maximized(start_maximized)
|
.with_has_shadow(true)
|
||||||
});
|
.with_maximized(start_maximized)
|
||||||
|
});
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
let window_config = window_config
|
let window_config = window_config
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ use std::collections::BTreeSet;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use freya::prelude::spawn_forever;
|
use freya::prelude::spawn_forever;
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
|
||||||
use oneclient_common::Patch;
|
use oneclient_common::Patch;
|
||||||
use oneclient_core::settings::store::save_settings_and_apply;
|
use oneclient_core::settings::store::save_settings_and_apply;
|
||||||
use oneclient_core::{LauncherState, ProfileUpdate};
|
use oneclient_core::{LauncherState, ProfileUpdate};
|
||||||
use oneclient_db::models::ClusterId;
|
use oneclient_db::models::ClusterId;
|
||||||
use oneclient_events::{Choice, Prompt, Signal};
|
use oneclient_events::{Choice, Prompt, Signal};
|
||||||
use oneclient_java::{JavaRuntime, JavaVendor};
|
use oneclient_java::{JavaRuntime, JavaVendor};
|
||||||
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
|
||||||
use crate::hooks::Actions;
|
use crate::hooks::Actions;
|
||||||
use crate::launcher;
|
use crate::launcher;
|
||||||
|
|
@ -59,7 +59,10 @@ async fn auto_install(refresh: UnboundedSender<()>) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::warn!(cluster_id, "could not read the cluster's Java version: {err:#}");
|
tracing::warn!(
|
||||||
|
cluster_id,
|
||||||
|
"could not read the cluster's Java version: {err:#}"
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -113,7 +116,10 @@ pub async fn offer_for_pinned_cluster(actions: &Actions, cluster_id: ClusterId)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::warn!(cluster_id, "could not read the cluster's Java version: {err:#}");
|
tracing::warn!(
|
||||||
|
cluster_id,
|
||||||
|
"could not read the cluster's Java version: {err:#}"
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use oneclient_events::{
|
|
||||||
Answer, Choice, Event, GroupedProgressEvent, Level, Notification, ProgressEvent, TaskCategory,
|
|
||||||
};
|
|
||||||
use oneclient_content::packages::ProviderId;
|
use oneclient_content::packages::ProviderId;
|
||||||
use oneclient_core::BrowserPackageUpdate;
|
use oneclient_core::BrowserPackageUpdate;
|
||||||
use oneclient_db::models::{ClusterId, OptionalModStatus};
|
use oneclient_db::models::{ClusterId, OptionalModStatus};
|
||||||
|
use oneclient_events::{
|
||||||
|
Answer, Choice, Event, GroupedProgressEvent, Level, Notification, ProgressEvent, TaskCategory,
|
||||||
|
};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
|
@ -80,9 +80,11 @@ impl OptionalModsGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offers(&self) -> impl Iterator<Item = (ClusterId, OptionalModRef)> + '_ {
|
pub fn offers(&self) -> impl Iterator<Item = (ClusterId, OptionalModRef)> + '_ {
|
||||||
self.mods
|
self.mods.iter().filter_map(move |item| {
|
||||||
.iter()
|
item.offer
|
||||||
.filter_map(move |item| item.offer.clone().map(|reference| (self.cluster_id, reference)))
|
.clone()
|
||||||
|
.map(|reference| (self.cluster_id, reference))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -274,8 +276,11 @@ impl GroupedTasks {
|
||||||
let done_count = self.done_count.get(&cat).copied().unwrap_or(0);
|
let done_count = self.done_count.get(&cat).copied().unwrap_or(0);
|
||||||
let reserved_units = self.reserved_units.get(&cat).copied().unwrap_or(0);
|
let reserved_units = self.reserved_units.get(&cat).copied().unwrap_or(0);
|
||||||
|
|
||||||
let live: Vec<&ChildRec> =
|
let live: Vec<&ChildRec> = self
|
||||||
self.children.values().filter(|c| c.category == cat).collect();
|
.children
|
||||||
|
.values()
|
||||||
|
.filter(|c| c.category == cat)
|
||||||
|
.collect();
|
||||||
let live_current: u64 = live.iter().map(|c| c.current.min(c.total)).sum();
|
let live_current: u64 = live.iter().map(|c| c.current.min(c.total)).sum();
|
||||||
let live_total: u64 = live.iter().map(|c| c.total).sum();
|
let live_total: u64 = live.iter().map(|c| c.total).sum();
|
||||||
|
|
||||||
|
|
@ -471,7 +476,7 @@ impl NotificationState {
|
||||||
);
|
);
|
||||||
self.push_ephemeral_toast(entry_id, MESSAGE_TOAST_TTL);
|
self.push_ephemeral_toast(entry_id, MESSAGE_TOAST_TTL);
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::Progress(ProgressEvent::Update { id, .. })
|
Event::Progress(ProgressEvent::Update { id, .. })
|
||||||
if id == oneclient_auth::MICROSOFT_LOGIN_PROGRESS => {}
|
if id == oneclient_auth::MICROSOFT_LOGIN_PROGRESS => {}
|
||||||
Event::Progress(ProgressEvent::Update { id, .. })
|
Event::Progress(ProgressEvent::Update { id, .. })
|
||||||
|
|
@ -741,8 +746,7 @@ impl NotificationState {
|
||||||
self.update_inbox_entry(inbox, entry_id, title, body, None, false);
|
self.update_inbox_entry(inbox, entry_id, title, body, None, false);
|
||||||
self.ensure_progress_toast(entry_id);
|
self.ensure_progress_toast(entry_id);
|
||||||
} else {
|
} else {
|
||||||
let entry_id =
|
let entry_id = self.push_inbox(inbox, title, body, Level::Info, None, false);
|
||||||
self.push_inbox(inbox, title, body, Level::Info, None, false);
|
|
||||||
self.push_ephemeral_toast(entry_id, MESSAGE_TOAST_TTL);
|
self.push_ephemeral_toast(entry_id, MESSAGE_TOAST_TTL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1017,7 +1021,10 @@ mod package_update_tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
state.resolve_package_update(1, "b");
|
state.resolve_package_update(1, "b");
|
||||||
assert!(wait.try_recv().is_ok(), "the last answer releases the launch");
|
assert!(
|
||||||
|
wait.try_recv().is_ok(),
|
||||||
|
"the last answer releases the launch"
|
||||||
|
);
|
||||||
assert!(state.package_updates.is_none());
|
assert!(state.package_updates.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1042,7 +1049,10 @@ mod package_update_tests {
|
||||||
|
|
||||||
state.open_package_updates(vec![group(&[])], Some(done));
|
state.open_package_updates(vec![group(&[])], Some(done));
|
||||||
|
|
||||||
assert!(wait.try_recv().is_ok(), "a modal that never opens cannot be answered");
|
assert!(
|
||||||
|
wait.try_recv().is_ok(),
|
||||||
|
"a modal that never opens cannot be answered"
|
||||||
|
);
|
||||||
assert!(state.package_updates.is_none());
|
assert!(state.package_updates.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1055,7 +1065,10 @@ mod package_update_tests {
|
||||||
state.open_package_updates(vec![group(&["a"])], Some(first));
|
state.open_package_updates(vec![group(&["a"])], Some(first));
|
||||||
state.open_package_updates(vec![group(&["b"])], Some(second));
|
state.open_package_updates(vec![group(&["b"])], Some(second));
|
||||||
|
|
||||||
assert!(first_wait.try_recv().is_ok(), "the replaced launch is let go");
|
assert!(
|
||||||
|
first_wait.try_recv().is_ok(),
|
||||||
|
"the replaced launch is let go"
|
||||||
|
);
|
||||||
assert!(second_wait.try_recv().is_err(), "the new one still waits");
|
assert!(second_wait.try_recv().is_err(), "the new one still waits");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn a_folder_survives_the_round_trip() {
|
fn a_folder_survives_the_round_trip() {
|
||||||
let folder = "26.1.2 Fabric";
|
let folder = "26.1.2 Fabric";
|
||||||
assert_eq!(parse_launch_url(&launch_url(folder)).as_deref(), Some(folder));
|
assert_eq!(
|
||||||
|
parse_launch_url(&launch_url(folder)).as_deref(),
|
||||||
|
Some(folder)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,7 @@ pub fn write_string(subkey: &str, name: Option<&str>, value: &str) -> Result<()>
|
||||||
let bytes = unsafe { std::slice::from_raw_parts(wide.as_ptr().cast::<u8>(), wide.len() * 2) };
|
let bytes = unsafe { std::slice::from_raw_parts(wide.as_ptr().cast::<u8>(), wide.len() * 2) };
|
||||||
|
|
||||||
let name = name.map(HSTRING::from);
|
let name = name.map(HSTRING::from);
|
||||||
let status =
|
let status = unsafe { RegSetValueExW(key, name_ptr(name.as_ref()), None, REG_SZ, Some(bytes)) };
|
||||||
unsafe { RegSetValueExW(key, name_ptr(name.as_ref()), None, REG_SZ, Some(bytes)) };
|
|
||||||
close(key);
|
close(key);
|
||||||
|
|
||||||
check(status, "writing the registry value")
|
check(status, "writing the registry value")
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,9 @@ fn retry(actions: &Actions) {
|
||||||
let events = actions.events();
|
let events = actions.events();
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut guard = station.clone().write_channel(crate::state::AppChannel::Launcher);
|
let mut guard = station
|
||||||
|
.clone()
|
||||||
|
.write_channel(crate::state::AppChannel::Launcher);
|
||||||
guard.launcher.error = None;
|
guard.launcher.error = None;
|
||||||
guard.launcher.snapshots = snapshots().len();
|
guard.launcher.snapshots = snapshots().len();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_bare_binary_has_no_bundle() {
|
fn a_bare_binary_has_no_bundle() {
|
||||||
assert_eq!(bundle_root(&PathBuf::from("/usr/local/bin/oneclient_app")), None);
|
assert_eq!(
|
||||||
|
bundle_root(&PathBuf::from("/usr/local/bin/oneclient_app")),
|
||||||
|
None
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bundle_root(&PathBuf::from("/tmp/target/debug/oneclient_app")),
|
bundle_root(&PathBuf::from("/tmp/target/debug/oneclient_app")),
|
||||||
None,
|
None,
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,11 @@ pub fn create(request: &ShortcutRequest) -> Result<PathBuf> {
|
||||||
std::fs::create_dir_all(&request.dir)
|
std::fs::create_dir_all(&request.dir)
|
||||||
.with_context(|| format!("couldn't open {}", request.dir.display()))?;
|
.with_context(|| format!("couldn't open {}", request.dir.display()))?;
|
||||||
|
|
||||||
let path = unique_path(&request.dir, &file_stem(&request.cluster_name), imp::EXTENSION)?;
|
let path = unique_path(
|
||||||
|
&request.dir,
|
||||||
|
&file_stem(&request.cluster_name),
|
||||||
|
imp::EXTENSION,
|
||||||
|
)?;
|
||||||
imp::write(request, &exe, &path)
|
imp::write(request, &exe, &path)
|
||||||
.with_context(|| format!("couldn't write {}", path.display()))?;
|
.with_context(|| format!("couldn't write {}", path.display()))?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,13 +106,15 @@ async fn run_check(auto_install: bool, events: EventBus) -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_self_update() -> bool {
|
fn can_self_update() -> bool {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if std::env::var_os("ONECLIENT_DISABLE_AUTOUPDATE").is_some_and(|val| val.eq_ignore_ascii_case("1")) {
|
if std::env::var_os("ONECLIENT_DISABLE_AUTOUPDATE")
|
||||||
return false;
|
.is_some_and(|val| val.eq_ignore_ascii_case("1"))
|
||||||
}
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ use std::cmp::Ordering;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use chrono::{Datelike, NaiveDate};
|
use chrono::{Datelike, NaiveDate};
|
||||||
use oneclient_core::clusters::Cluster;
|
|
||||||
use oneclient_common::domain::GameLoader;
|
use oneclient_common::domain::GameLoader;
|
||||||
use oneclient_common::{ParsedMcVersion, VersionKey, format_mc_version, parse_mc_version};
|
use oneclient_common::{ParsedMcVersion, VersionKey, format_mc_version, parse_mc_version};
|
||||||
|
use oneclient_core::clusters::Cluster;
|
||||||
|
|
||||||
use oneclient_common::MEMORY_HEADROOM_GB;
|
use oneclient_common::MEMORY_HEADROOM_GB;
|
||||||
pub use oneclient_common::total_ram_mb;
|
pub use oneclient_common::total_ram_mb;
|
||||||
|
|
@ -207,7 +207,7 @@ pub fn format_res((w, h): (u32, u32)) -> String {
|
||||||
format!("{w}×{h}")
|
format!("{w}×{h}")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prevents user from choosing his max amount of ram preset (e.g Someone has 16GB of RAM,
|
/// Prevents user from choosing his max amount of ram preset (e.g Someone has 16GB of RAM,
|
||||||
/// so the max preset is 16GB - 2GB = 14GB)
|
/// so the max preset is 16GB - 2GB = 14GB)
|
||||||
const MEMORY_PRESETS_GB: [u32; 11] = [2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64];
|
const MEMORY_PRESETS_GB: [u32; 11] = [2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,9 +82,7 @@ impl Component for PlaytimeChart {
|
||||||
"Playtime by day",
|
"Playtime by day",
|
||||||
format!("Every {day} you have played, added up"),
|
format!("Every {day} you have played, added up"),
|
||||||
BarChart::new(self.per_weekday.clone(), weekday_labels())
|
BarChart::new(self.per_weekday.clone(), weekday_labels())
|
||||||
.readout_labels(
|
.readout_labels(WEEKDAY_FULL.iter().map(|d| format!("All {d}s")).collect())
|
||||||
WEEKDAY_FULL.iter().map(|d| format!("All {d}s")).collect(),
|
|
||||||
)
|
|
||||||
.highlight(self.peak_weekday)
|
.highlight(self.peak_weekday)
|
||||||
.unit(ValueUnit::Duration)
|
.unit(ValueUnit::Duration)
|
||||||
.gap(6.)
|
.gap(6.)
|
||||||
|
|
@ -241,15 +239,15 @@ impl Component for DailyChart {
|
||||||
.child(DateRangePicker::new(custom, (first_day, last_day)).height(30.))
|
.child(DateRangePicker::new(custom, (first_day, last_day)).height(30.))
|
||||||
.into_element();
|
.into_element();
|
||||||
|
|
||||||
chart_card(
|
chart_card(
|
||||||
"Daily playtime",
|
"Daily playtime",
|
||||||
subtitle,
|
subtitle,
|
||||||
Some(nav),
|
Some(nav),
|
||||||
BarChart::new(values, labels)
|
BarChart::new(values, labels)
|
||||||
.unit(ValueUnit::Duration)
|
.unit(ValueUnit::Duration)
|
||||||
.gap(3.)
|
.gap(3.)
|
||||||
.into_element(),
|
.into_element(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -293,10 +291,8 @@ fn monthly_series(daily: &[DayPlaytime]) -> Vec<(NaiveDate, i64)> {
|
||||||
*totals.entry(month).or_insert(0) += day.secs;
|
*totals.entry(month).or_insert(0) += day.secs;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (Some(first), Some(last)) = (
|
let (Some(first), Some(last)) = (totals.keys().min().copied(), totals.keys().max().copied())
|
||||||
totals.keys().min().copied(),
|
else {
|
||||||
totals.keys().max().copied(),
|
|
||||||
) else {
|
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -278,4 +278,3 @@ fn persona_icon(persona: Persona) -> IconType {
|
||||||
Persona::Sprinter => IconType::Play,
|
Persona::Sprinter => IconType::Play,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -175,13 +175,7 @@ pub(crate) fn activity_badge(active: bool) -> impl IntoElement {
|
||||||
.padding(Gaps::new_symmetric(2., 8.))
|
.padding(Gaps::new_symmetric(2., 8.))
|
||||||
.corner_radius(CornerRadius::new_all(999.))
|
.corner_radius(CornerRadius::new_all(999.))
|
||||||
.background(color.with_a(38))
|
.background(color.with_a(38))
|
||||||
.child(
|
.child(label().text(text).font_size(11.).max_lines(1).color(color))
|
||||||
label()
|
|
||||||
.text(text)
|
|
||||||
.font_size(11.)
|
|
||||||
.max_lines(1)
|
|
||||||
.color(color),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn badge(
|
fn badge(
|
||||||
|
|
@ -444,7 +438,10 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let sodium = entry(&map, "sodium");
|
let sodium = entry(&map, "sodium");
|
||||||
assert!(sodium.is_version("v1"), "the older version is still in there");
|
assert!(
|
||||||
|
sodium.is_version("v1"),
|
||||||
|
"the older version is still in there"
|
||||||
|
);
|
||||||
assert!(sodium.is_version("v2"));
|
assert!(sodium.is_version("v2"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sodium.find_version("v2").and_then(|v| v.hash.clone()),
|
sodium.find_version("v2").and_then(|v| v.hash.clone()),
|
||||||
|
|
@ -464,7 +461,11 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let sodium = entry(&map, "sodium");
|
let sodium = entry(&map, "sodium");
|
||||||
assert_eq!(sodium.source, InstallSource::Bundled, "the bundle owns the project");
|
assert_eq!(
|
||||||
|
sodium.source,
|
||||||
|
InstallSource::Bundled,
|
||||||
|
"the bundle owns the project"
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sodium.find_version("v1").map(|v| v.source),
|
sodium.find_version("v1").map(|v| v.source),
|
||||||
Some(InstallSource::Bundled)
|
Some(InstallSource::Bundled)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use freya::prelude::*;
|
||||||
use oneclient_content::packages::{ContentType, ProviderId};
|
use oneclient_content::packages::{ContentType, ProviderId};
|
||||||
|
|
||||||
use crate::components::ScrollArea;
|
use crate::components::ScrollArea;
|
||||||
|
use crate::hooks::use_cluster;
|
||||||
use crate::hooks::{
|
use crate::hooks::{
|
||||||
bundles_with_status_items, cluster_content_items, content_type_for_slug, project_detail,
|
bundles_with_status_items, cluster_content_items, content_type_for_slug, project_detail,
|
||||||
use_browser_compat, use_bundles_with_status, use_cluster_content, use_dispatch,
|
use_browser_compat, use_bundles_with_status, use_cluster_content, use_dispatch,
|
||||||
|
|
@ -10,7 +11,6 @@ use crate::hooks::{
|
||||||
};
|
};
|
||||||
use crate::theme::colors;
|
use crate::theme::colors;
|
||||||
use crate::ui::border_all_color;
|
use crate::ui::border_all_color;
|
||||||
use crate::hooks::use_cluster;
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Installed, InstalledVersion, PackageBanner, Thumbnail, activity_badge, installed_badge,
|
Installed, InstalledVersion, PackageBanner, Thumbnail, activity_badge, installed_badge,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ use oneclient_content::packages::markdown::normalize_markdown;
|
||||||
use oneclient_content::packages::types::{PackageBody, ProjectDetail, ReleaseType, VersionSummary};
|
use oneclient_content::packages::types::{PackageBody, ProjectDetail, ReleaseType, VersionSummary};
|
||||||
|
|
||||||
use crate::Actions;
|
use crate::Actions;
|
||||||
use crate::components::{Button, Icon, IconType, Markdown, MarkdownStyle, Segment, SegmentedControl};
|
use crate::components::{
|
||||||
|
Button, Icon, IconType, Markdown, MarkdownStyle, Segment, SegmentedControl,
|
||||||
|
};
|
||||||
use crate::hooks::VERSIONS_PAGE_SIZE;
|
use crate::hooks::VERSIONS_PAGE_SIZE;
|
||||||
use crate::theme::colors;
|
use crate::theme::colors;
|
||||||
use crate::ui::border_all_color;
|
use crate::ui::border_all_color;
|
||||||
|
|
@ -283,14 +285,7 @@ fn version_row(
|
||||||
.map(|installed| activity_badge(installed.enabled).into_element()),
|
.map(|installed| activity_badge(installed.enabled).into_element()),
|
||||||
)
|
)
|
||||||
.child(version_button(
|
.child(version_button(
|
||||||
installed,
|
installed, v.name, provider, project_id, version_id, cluster_id, dispatch, installing,
|
||||||
v.name,
|
|
||||||
provider,
|
|
||||||
project_id,
|
|
||||||
version_id,
|
|
||||||
cluster_id,
|
|
||||||
dispatch,
|
|
||||||
installing,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -323,7 +318,11 @@ fn version_button(
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(hash) = installed.hash else {
|
let Some(hash) = installed.hash else {
|
||||||
return Button::new().secondary().small().enabled(false).text("Install");
|
return Button::new()
|
||||||
|
.secondary()
|
||||||
|
.small()
|
||||||
|
.enabled(false)
|
||||||
|
.text("Install");
|
||||||
};
|
};
|
||||||
|
|
||||||
Button::new()
|
Button::new()
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
use freya::prelude::*;
|
use freya::prelude::*;
|
||||||
use oneclient_common::Patch;
|
use oneclient_common::Patch;
|
||||||
use oneclient_java::JavaRuntime;
|
|
||||||
use oneclient_common::domain::GameLoader;
|
use oneclient_common::domain::GameLoader;
|
||||||
use oneclient_core::settings::{
|
use oneclient_core::settings::{GameSettingsProfile, PackageUpdateMode, ProfileUpdate, Resolution};
|
||||||
GameSettingsProfile, PackageUpdateMode, ProfileUpdate, Resolution,
|
use oneclient_java::JavaRuntime;
|
||||||
};
|
|
||||||
|
|
||||||
use crate::components::{
|
use crate::components::{
|
||||||
Button, Dropdown, Icon, IconType, ScrollArea, TextInput, memory_field, toggle,
|
Button, Dropdown, Icon, IconType, ScrollArea, TextInput, memory_field, toggle,
|
||||||
|
|
@ -309,7 +307,11 @@ impl Component for VerifyFilesRow {
|
||||||
.secondary()
|
.secondary()
|
||||||
.enabled(!running)
|
.enabled(!running)
|
||||||
.maybe(!running, |el| el.on_press(on_press))
|
.maybe(!running, |el| el.on_press(on_press))
|
||||||
.text(if running { "Verifying..." } else { "Verify Files" });
|
.text(if running {
|
||||||
|
"Verifying..."
|
||||||
|
} else {
|
||||||
|
"Verify Files"
|
||||||
|
});
|
||||||
|
|
||||||
settings_row(
|
settings_row(
|
||||||
IconType::ClipboardCheck,
|
IconType::ClipboardCheck,
|
||||||
|
|
|
||||||
|
|
@ -336,9 +336,7 @@ pub fn viewer_header(
|
||||||
)
|
)
|
||||||
.into_element(),
|
.into_element(),
|
||||||
)
|
)
|
||||||
.child(
|
.child(UploadToMclogs::new(has_log, confirm));
|
||||||
UploadToMclogs::new(has_log, confirm)
|
|
||||||
);
|
|
||||||
|
|
||||||
rect()
|
rect()
|
||||||
.vertical()
|
.vertical()
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ use crate::hooks::{
|
||||||
};
|
};
|
||||||
use crate::layout::cluster_content;
|
use crate::layout::cluster_content;
|
||||||
|
|
||||||
|
use super::cluster_not_found;
|
||||||
use super::package_manager::{
|
use super::package_manager::{
|
||||||
PackageManager, bundle_categories, bundle_packages, use_content_meta,
|
PackageManager, bundle_categories, bundle_packages, use_content_meta,
|
||||||
};
|
};
|
||||||
use super::cluster_not_found;
|
|
||||||
use crate::hooks::use_cluster;
|
use crate::hooks::use_cluster;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::components::ScrollArea;
|
||||||
use crate::hooks::{try_cluster_analytics, use_cluster_analytics};
|
use crate::hooks::{try_cluster_analytics, use_cluster_analytics};
|
||||||
use crate::layout::cluster_content;
|
use crate::layout::cluster_content;
|
||||||
use crate::theme::colors;
|
use crate::theme::colors;
|
||||||
use crate::ui::{centered_note};
|
use crate::ui::centered_note;
|
||||||
use crate::view::app::{analytics_body, analytics_placeholder};
|
use crate::view::app::{analytics_body, analytics_placeholder};
|
||||||
|
|
||||||
use super::cluster_not_found;
|
use super::cluster_not_found;
|
||||||
|
|
|
||||||
|
|
@ -74,10 +74,7 @@ pub fn use_content_meta(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let local = use_package_meta_batch(
|
let local = use_package_meta_batch(ProviderId::Local, local_project_ids(content, content_type));
|
||||||
ProviderId::Local,
|
|
||||||
local_project_ids(content, content_type),
|
|
||||||
);
|
|
||||||
for (hash, meta) in package_meta_batch(&local) {
|
for (hash, meta) in package_meta_batch(&local) {
|
||||||
out.insert((ProviderId::Local, hash), meta);
|
out.insert((ProviderId::Local, hash), meta);
|
||||||
}
|
}
|
||||||
|
|
@ -257,9 +254,7 @@ fn make_row(
|
||||||
hash: installed_info.map(|i| i.hash.clone()),
|
hash: installed_info.map(|i| i.hash.clone()),
|
||||||
update_available,
|
update_available,
|
||||||
hidden,
|
hidden,
|
||||||
seen_status: installed_info
|
seen_status: installed_info.map(|i| i.seen_status).unwrap_or_default(),
|
||||||
.map(|i| i.seen_status)
|
|
||||||
.unwrap_or_default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -405,7 +400,10 @@ impl Component for PackageManager {
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(cleared) if cleared > 0 => {
|
Ok(cleared) if cleared > 0 => {
|
||||||
tracing::debug!(cleared, "retired package badges after the list was viewed");
|
tracing::debug!(
|
||||||
|
cleared,
|
||||||
|
"retired package badges after the list was viewed"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => tracing::warn!(%err, "failed to retire package badges"),
|
Err(err) => tracing::warn!(%err, "failed to retire package badges"),
|
||||||
|
|
|
||||||
|
|
@ -170,19 +170,11 @@ pub(super) fn toolbar_bar(
|
||||||
} else {
|
} else {
|
||||||
Size::flex(1.0)
|
Size::flex(1.0)
|
||||||
})
|
})
|
||||||
.height(if stacked {
|
.height(if stacked { Size::fill() } else { Size::auto() })
|
||||||
Size::fill()
|
|
||||||
} else {
|
|
||||||
Size::auto()
|
|
||||||
})
|
|
||||||
.child(
|
.child(
|
||||||
TabBar::new()
|
TabBar::new()
|
||||||
.width(Size::auto())
|
.width(Size::auto())
|
||||||
.height(if stacked {
|
.height(if stacked { Size::fill() } else { Size::auto() })
|
||||||
Size::fill()
|
|
||||||
} else {
|
|
||||||
Size::auto()
|
|
||||||
})
|
|
||||||
.spacing(20.)
|
.spacing(20.)
|
||||||
.font_size(12.)
|
.font_size(12.)
|
||||||
.tabs(tab_items),
|
.tabs(tab_items),
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,11 @@ impl Component for ProcessLogs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_lines(lines: &Arc<Vec<Arc<str>>>, search: &str, level: LevelFilter) -> Arc<Vec<Arc<str>>> {
|
fn filter_lines(
|
||||||
|
lines: &Arc<Vec<Arc<str>>>,
|
||||||
|
search: &str,
|
||||||
|
level: LevelFilter,
|
||||||
|
) -> Arc<Vec<Arc<str>>> {
|
||||||
let needle = (!search.trim().is_empty()).then(|| search.to_lowercase());
|
let needle = (!search.trim().is_empty()).then(|| search.to_lowercase());
|
||||||
let wanted = level.to_level();
|
let wanted = level.to_level();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,10 @@ use crate::components::{
|
||||||
Button, Icon, IconType, LocalImage, OverlayPopup, ScreenshotViewer, ScrollArea, Segment,
|
Button, Icon, IconType, LocalImage, OverlayPopup, ScreenshotViewer, ScrollArea, Segment,
|
||||||
SegmentedControl, open_folder_button, screenshot_context_menu,
|
SegmentedControl, open_folder_button, screenshot_context_menu,
|
||||||
};
|
};
|
||||||
use crate::hooks::{ScreenshotAction, query_is_loading, try_cluster_screenshots, use_cluster_screenshots, use_dispatch, use_screenshot_action, use_screenshot_folder_watch, use_view_state};
|
use crate::hooks::{
|
||||||
|
ScreenshotAction, query_is_loading, try_cluster_screenshots, use_cluster_screenshots,
|
||||||
|
use_dispatch, use_screenshot_action, use_screenshot_folder_watch, use_view_state,
|
||||||
|
};
|
||||||
use crate::layout::cluster_content;
|
use crate::layout::cluster_content;
|
||||||
use crate::theme::colors;
|
use crate::theme::colors;
|
||||||
use crate::ui::{border_all_color, flow_grid, fmt_date, grid_columns_for_width};
|
use crate::ui::{border_all_color, flow_grid, fmt_date, grid_columns_for_width};
|
||||||
|
|
@ -65,8 +68,7 @@ impl Component for ClusterScreenshots {
|
||||||
);
|
);
|
||||||
|
|
||||||
let content: Element = if shots.is_empty() {
|
let content: Element = if shots.is_empty() {
|
||||||
empty_state(query_is_loading(&query))
|
empty_state(query_is_loading(&query)).into_element()
|
||||||
.into_element()
|
|
||||||
} else {
|
} else {
|
||||||
let mut items: Vec<Element> = Vec::new();
|
let mut items: Vec<Element> = Vec::new();
|
||||||
for (idx, info) in shots.iter().enumerate() {
|
for (idx, info) in shots.iter().enumerate() {
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ use crate::hooks::{
|
||||||
};
|
};
|
||||||
use crate::layout::cluster_content;
|
use crate::layout::cluster_content;
|
||||||
|
|
||||||
|
use super::cluster_not_found;
|
||||||
use super::package_manager::{
|
use super::package_manager::{
|
||||||
PackageManager, bundle_categories, bundle_packages, use_content_meta,
|
PackageManager, bundle_categories, bundle_packages, use_content_meta,
|
||||||
};
|
};
|
||||||
use super::cluster_not_found;
|
|
||||||
use crate::hooks::use_cluster;
|
use crate::hooks::use_cluster;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ use crate::hooks::{
|
||||||
};
|
};
|
||||||
use crate::layout::cluster_content;
|
use crate::layout::cluster_content;
|
||||||
|
|
||||||
|
use super::cluster_not_found;
|
||||||
use super::package_manager::{
|
use super::package_manager::{
|
||||||
PackageManager, bundle_categories, bundle_packages, use_content_meta,
|
PackageManager, bundle_categories, bundle_packages, use_content_meta,
|
||||||
};
|
};
|
||||||
use super::cluster_not_found;
|
|
||||||
use crate::hooks::use_cluster;
|
use crate::hooks::use_cluster;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
use freya::prelude::*;
|
use freya::prelude::*;
|
||||||
use freya::router::RouterContext;
|
use freya::router::RouterContext;
|
||||||
use oneclient_common::VersionKey;
|
use oneclient_common::VersionKey;
|
||||||
use oneclient_core::clusters::Cluster;
|
|
||||||
use oneclient_common::domain::GameLoader;
|
use oneclient_common::domain::GameLoader;
|
||||||
|
use oneclient_core::clusters::Cluster;
|
||||||
|
|
||||||
use crate::components::{
|
use crate::components::{
|
||||||
Button, ClusterLandscapeArt, Dropdown, Icon, IconType, ScrollArea, VersionCard,
|
Button, ClusterLandscapeArt, Dropdown, Icon, IconType, ScrollArea, VersionCard,
|
||||||
};
|
};
|
||||||
use crate::hooks::{settled_or_loading, use_active_cluster_id, use_clusters, use_dispatch, use_game_snapshot, use_launcher, use_version_metadata};
|
use crate::hooks::{
|
||||||
|
settled_or_loading, use_active_cluster_id, use_clusters, use_dispatch, use_game_snapshot,
|
||||||
|
use_launcher, use_version_metadata,
|
||||||
|
};
|
||||||
use crate::routes::Route;
|
use crate::routes::Route;
|
||||||
use crate::theme::colors;
|
use crate::theme::colors;
|
||||||
use crate::ui::border_all_color;
|
use crate::ui::border_all_color;
|
||||||
|
|
@ -294,7 +297,12 @@ impl Component for DetailSidebar {
|
||||||
.color(colors::fg_secondary()),
|
.color(colors::fg_secondary()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.children(version_rows(self.line, &keys, version_value, self.selected_version))
|
.children(version_rows(
|
||||||
|
self.line,
|
||||||
|
&keys,
|
||||||
|
version_value,
|
||||||
|
self.selected_version,
|
||||||
|
))
|
||||||
.children(loader_rows(&loaders, loader_value, self.selected_loader)),
|
.children(loader_rows(&loaders, loader_value, self.selected_loader)),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ pub struct Debug;
|
||||||
|
|
||||||
impl Component for Debug {
|
impl Component for Debug {
|
||||||
fn render(&self) -> impl IntoElement {
|
fn render(&self) -> impl IntoElement {
|
||||||
let dispatch = use_dispatch();
|
let dispatch = use_dispatch();
|
||||||
let log_debug_info = use_state(|| false);
|
let log_debug_info = use_state(|| false);
|
||||||
let show_dev_stuff = use_state(|| false);
|
let show_dev_stuff = use_state(|| false);
|
||||||
let seen_onboarding = use_state(|| true);
|
let seen_onboarding = use_state(|| true);
|
||||||
|
|
@ -103,12 +103,15 @@ impl Component for Debug {
|
||||||
.child(divider())
|
.child(divider())
|
||||||
.child(section(
|
.child(section(
|
||||||
"Other",
|
"Other",
|
||||||
vec![action_row(&dispatch, vec![
|
vec![action_row(
|
||||||
("Open Dev Tools", IconType::CodeSnippet02),
|
&dispatch,
|
||||||
("Open Onboarding", IconType::Rocket02),
|
vec![
|
||||||
("Open Launcher Data", IconType::Folder),
|
("Open Dev Tools", IconType::CodeSnippet02),
|
||||||
("Log Running Processes", IconType::Terminal),
|
("Open Onboarding", IconType::Rocket02),
|
||||||
])],
|
("Open Launcher Data", IconType::Folder),
|
||||||
|
("Log Running Processes", IconType::Terminal),
|
||||||
|
],
|
||||||
|
)],
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -432,52 +435,50 @@ const CLUSTER_UPDATE_PRESETS: [(&str, IconType, ClusterUpdatePreset); 7] = [
|
||||||
&[],
|
&[],
|
||||||
),
|
),
|
||||||
preset_summary(2, "Skyblock", &[], &[], &["OptiFine", "Skytils"], &[]),
|
preset_summary(2, "Skyblock", &[], &[], &["OptiFine", "Skytils"], &[]),
|
||||||
preset_summary(3, "Vanilla+", &[], &["Sodium", "Iris", "FerriteCore"], &[], &[]),
|
preset_summary(
|
||||||
|
3,
|
||||||
|
"Vanilla+",
|
||||||
|
&[],
|
||||||
|
&["Sodium", "Iris", "FerriteCore"],
|
||||||
|
&[],
|
||||||
|
&[],
|
||||||
|
),
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
("2 clusters · removals only", IconType::Trash01, || {
|
("2 clusters · removals only", IconType::Trash01, || {
|
||||||
vec![
|
vec![
|
||||||
preset_summary(1, "PolyBlock", &[], &[], &["OptiFine"], &[]),
|
preset_summary(1, "PolyBlock", &[], &[], &["OptiFine"], &[]),
|
||||||
preset_summary(2, "Skyblock", &[], &[], &["Skytils", "NotEnoughUpdates"], &[]),
|
preset_summary(
|
||||||
|
2,
|
||||||
|
"Skyblock",
|
||||||
|
&[],
|
||||||
|
&[],
|
||||||
|
&["Skytils", "NotEnoughUpdates"],
|
||||||
|
&[],
|
||||||
|
),
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
("6 clusters · long names", IconType::Database01, || {
|
("6 clusters · long names", IconType::Database01, || {
|
||||||
(1..=6)
|
(1..=6)
|
||||||
.map(|i| {
|
.map(|i| ClusterUpdateSummary {
|
||||||
ClusterUpdateSummary {
|
cluster_id: i,
|
||||||
cluster_id: i,
|
cluster_name: format!(
|
||||||
cluster_name: format!(
|
"Cluster {i} with a deliberately overlong name that has to truncate"
|
||||||
"Cluster {i} with a deliberately overlong name that has to truncate"
|
),
|
||||||
),
|
updated: cluster_update_items(&[
|
||||||
updated: cluster_update_items(&[
|
format!("Sodium 0.{i} → 0.{}", i + 1),
|
||||||
format!("Sodium 0.{i} → 0.{}", i + 1),
|
format!("Iris 1.{i} → 1.{}", i + 1),
|
||||||
format!("Iris 1.{i} → 1.{}", i + 1),
|
]),
|
||||||
]),
|
added: cluster_update_items(&[format!("Lithium {i}")]),
|
||||||
added: cluster_update_items(&[format!("Lithium {i}")]),
|
removed: Vec::new(),
|
||||||
removed: Vec::new(),
|
optional: Vec::new(),
|
||||||
optional: Vec::new(),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}),
|
}),
|
||||||
("2 clusters · offers only", IconType::Plus, || {
|
("2 clusters · offers only", IconType::Plus, || {
|
||||||
vec![
|
vec![
|
||||||
preset_summary(
|
preset_summary(1, "PolyBlock", &[], &[], &[], &["Lithium", "FerriteCore"]),
|
||||||
1,
|
preset_summary(2, "Skyblock", &[], &["Skytils"], &[], &["Skytils"]),
|
||||||
"PolyBlock",
|
|
||||||
&[],
|
|
||||||
&[],
|
|
||||||
&[],
|
|
||||||
&["Lithium", "FerriteCore"],
|
|
||||||
),
|
|
||||||
preset_summary(
|
|
||||||
2,
|
|
||||||
"Skyblock",
|
|
||||||
&[],
|
|
||||||
&["Skytils"],
|
|
||||||
&[],
|
|
||||||
&["Skytils"],
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
@ -519,7 +520,7 @@ fn preset_summary(
|
||||||
updated: cluster_update_items(updated),
|
updated: cluster_update_items(updated),
|
||||||
added: cluster_update_items(added),
|
added: cluster_update_items(added),
|
||||||
removed: cluster_update_items(removed),
|
removed: cluster_update_items(removed),
|
||||||
optional: cluster_update_items(optional)
|
optional: cluster_update_items(optional),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1036,9 +1037,11 @@ fn run_damage(dispatch: &crate::Actions, kind: DamageKind, cluster_id: i64) {
|
||||||
let dispatch = dispatch.clone();
|
let dispatch = dispatch.clone();
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
let result = match kind {
|
let result = match kind {
|
||||||
DamageKind::Assets(count, damage) => oneclient_core::simulate::damage_assets(count, damage)
|
DamageKind::Assets(count, damage) => {
|
||||||
.await
|
oneclient_core::simulate::damage_assets(count, damage)
|
||||||
.map(|report| (report, damage.verb())),
|
.await
|
||||||
|
.map(|report| (report, damage.verb()))
|
||||||
|
}
|
||||||
DamageKind::Libraries(count, damage) => {
|
DamageKind::Libraries(count, damage) => {
|
||||||
oneclient_core::simulate::damage_libraries(count, damage)
|
oneclient_core::simulate::damage_libraries(count, damage)
|
||||||
.await
|
.await
|
||||||
|
|
@ -1300,9 +1303,9 @@ fn action_row(dispatch: &Actions, buttons: Vec<(&'static str, IconType)>) -> Ele
|
||||||
.text(text);
|
.text(text);
|
||||||
|
|
||||||
if text == "Open Onboarding" {
|
if text == "Open Onboarding" {
|
||||||
let dispatch = dispatch.clone();
|
let dispatch = dispatch.clone();
|
||||||
button = button.on_press(move |_| {
|
button = button.on_press(move |_| {
|
||||||
dispatch.reset_onboarding();
|
dispatch.reset_onboarding();
|
||||||
let _ = RouterContext::get().replace(Route::OnboardingWelcome {});
|
let _ = RouterContext::get().replace(Route::OnboardingWelcome {});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -555,10 +555,10 @@ impl Component for AccountRow {
|
||||||
.enabled(false)
|
.enabled(false)
|
||||||
.tooltip("Edit")
|
.tooltip("Edit")
|
||||||
.child(
|
.child(
|
||||||
Icon::new(IconType::Pencil01)
|
Icon::new(IconType::Pencil01)
|
||||||
.size(18.)
|
.size(18.)
|
||||||
.color(colors::fg_secondary()),
|
.color(colors::fg_secondary()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new()
|
Button::new()
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,11 @@ impl Component for ReleaseCard {
|
||||||
.font_weight(FontWeight::SEMI_BOLD)
|
.font_weight(FontWeight::SEMI_BOLD)
|
||||||
.color(colors::fg_primary()),
|
.color(colors::fg_primary()),
|
||||||
)
|
)
|
||||||
.child(Icon::new(IconType::ChevronDown).size(18.).rotate(chevron_deg)),
|
.child(
|
||||||
|
Icon::new(IconType::ChevronDown)
|
||||||
|
.size(18.)
|
||||||
|
.rotate(chevron_deg),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.maybe_child(is_open.then(|| {
|
.maybe_child(is_open.then(|| {
|
||||||
rect()
|
rect()
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ use oneclient_java::{JavaRuntime, JavaVendor, is_launcher_managed};
|
||||||
use super::settings_page;
|
use super::settings_page;
|
||||||
use crate::components::{Button, Icon, IconType, JavaInstallManager, OverlayPopup, ScrollArea};
|
use crate::components::{Button, Icon, IconType, JavaInstallManager, OverlayPopup, ScrollArea};
|
||||||
use crate::hooks::{Actions, java_runtimes, use_dispatch, use_java_runtimes};
|
use crate::hooks::{Actions, java_runtimes, use_dispatch, use_java_runtimes};
|
||||||
use crate::invalidate_java_queries;
|
|
||||||
use crate::theme::colors;
|
use crate::theme::colors;
|
||||||
use crate::ui::border_all_color;
|
use crate::ui::border_all_color;
|
||||||
use crate::view::app::settings::section_header;
|
use crate::view::app::settings::section_header;
|
||||||
|
|
@ -23,19 +22,6 @@ impl Component for SettingsJava {
|
||||||
let mut show_manager = use_state(|| false);
|
let mut show_manager = use_state(|| false);
|
||||||
let pending_remove = use_state(|| None::<PendingRemove>);
|
let pending_remove = use_state(|| None::<PendingRemove>);
|
||||||
|
|
||||||
fn invalidate_runtimes(dispatch: Actions) {
|
|
||||||
spawn(async move {
|
|
||||||
invalidate_java_queries().await;
|
|
||||||
dispatch
|
|
||||||
.notify("Java runtimes refreshed")
|
|
||||||
.body("The installed runtime list is up to date")
|
|
||||||
.info()
|
|
||||||
.send();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let refresh_dispatch = dispatch.clone();
|
|
||||||
|
|
||||||
let mut shell = settings_page()
|
let mut shell = settings_page()
|
||||||
.child(section_header("ADD RUNTIME"))
|
.child(section_header("ADD RUNTIME"))
|
||||||
.child(AddRow { show_manager }.into_element())
|
.child(AddRow { show_manager }.into_element())
|
||||||
|
|
@ -56,7 +42,11 @@ impl Component for SettingsJava {
|
||||||
|
|
||||||
let pending = pending_remove.read().clone();
|
let pending = pending_remove.read().clone();
|
||||||
if let Some(target) = pending {
|
if let Some(target) = pending {
|
||||||
shell = shell.child(confirm_remove_modal(removing_dispatch, pending_remove, target))
|
shell = shell.child(confirm_remove_modal(
|
||||||
|
removing_dispatch,
|
||||||
|
pending_remove,
|
||||||
|
target,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
shell.into_element()
|
shell.into_element()
|
||||||
|
|
@ -141,7 +131,7 @@ fn runtimes_table(
|
||||||
RuntimeRow {
|
RuntimeRow {
|
||||||
runtime,
|
runtime,
|
||||||
last: idx + 1 == count,
|
last: idx + 1 == count,
|
||||||
pending_remove
|
pending_remove,
|
||||||
}
|
}
|
||||||
.into_element(),
|
.into_element(),
|
||||||
);
|
);
|
||||||
|
|
@ -191,7 +181,7 @@ fn table_header() -> impl IntoElement {
|
||||||
struct RuntimeRow {
|
struct RuntimeRow {
|
||||||
runtime: JavaRuntime,
|
runtime: JavaRuntime,
|
||||||
last: bool,
|
last: bool,
|
||||||
pending_remove: State<Option<PendingRemove>>
|
pending_remove: State<Option<PendingRemove>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for RuntimeRow {
|
impl Component for RuntimeRow {
|
||||||
|
|
@ -303,7 +293,7 @@ fn confirm_remove_modal(
|
||||||
Button::new().primary().text("Remove from list")
|
Button::new().primary().text("Remove from list")
|
||||||
};
|
};
|
||||||
|
|
||||||
OverlayPopup::new()
|
OverlayPopup::new()
|
||||||
.on_close(move |()| pending.set(None))
|
.on_close(move |()| pending.set(None))
|
||||||
.child(
|
.child(
|
||||||
rect()
|
rect()
|
||||||
|
|
@ -359,7 +349,7 @@ fn confirm_remove_modal(
|
||||||
pending.set(None);
|
pending.set(None);
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.into_element()
|
.into_element()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,11 @@ impl Component for DataFolder {
|
||||||
.small()
|
.small()
|
||||||
.disabled(checking_now)
|
.disabled(checking_now)
|
||||||
.on_press(move |_| browse(pending, error, checking))
|
.on_press(move |_| browse(pending, error, checking))
|
||||||
.text(if checking_now { "Checking…" } else { "Change…" });
|
.text(if checking_now {
|
||||||
|
"Checking…"
|
||||||
|
} else {
|
||||||
|
"Change…"
|
||||||
|
});
|
||||||
|
|
||||||
let mut buttons = Vec::new();
|
let mut buttons = Vec::new();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
use freya::prelude::*;
|
use freya::prelude::*;
|
||||||
use oneclient_common::Patch;
|
use oneclient_common::Patch;
|
||||||
use oneclient_core::settings::{PackageUpdateMode, ProfileUpdate, Resolution};
|
|
||||||
#[cfg(any(target_os = "linux", windows))]
|
#[cfg(any(target_os = "linux", windows))]
|
||||||
use oneclient_core::settings::SettingsOsExtra;
|
use oneclient_core::settings::SettingsOsExtra;
|
||||||
|
use oneclient_core::settings::{PackageUpdateMode, ProfileUpdate, Resolution};
|
||||||
|
|
||||||
use super::settings_page;
|
use super::settings_page;
|
||||||
|
#[cfg(any(target_os = "linux", windows))]
|
||||||
|
use crate::components::toggle_controlled;
|
||||||
use crate::components::{
|
use crate::components::{
|
||||||
Dropdown, Icon, IconType, TextInput, memory_field, toggle, validate_number,
|
Dropdown, Icon, IconType, TextInput, memory_field, toggle, validate_number,
|
||||||
};
|
};
|
||||||
#[cfg(any(target_os = "linux", windows))]
|
|
||||||
use crate::components::toggle_controlled;
|
|
||||||
use crate::hooks::{use_dispatch, use_settings_snapshot};
|
use crate::hooks::{use_dispatch, use_settings_snapshot};
|
||||||
use crate::theme::colors;
|
use crate::theme::colors;
|
||||||
use crate::view::app::settings::{section_header, settings_row};
|
use crate::view::app::settings::{section_header, settings_row};
|
||||||
|
|
@ -219,10 +219,7 @@ fn build_update(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dispatched separately from [`build_update`] which debounces keystrokes a dropdown has no intermediate states
|
/// Dispatched separately from [`build_update`] which debounces keystrokes a dropdown has no intermediate states
|
||||||
fn update_mode_field(
|
fn update_mode_field(selected: PackageUpdateMode, dispatch: crate::Actions) -> impl IntoElement {
|
||||||
selected: PackageUpdateMode,
|
|
||||||
dispatch: crate::Actions,
|
|
||||||
) -> impl IntoElement {
|
|
||||||
let options: Vec<String> = PackageUpdateMode::ALL
|
let options: Vec<String> = PackageUpdateMode::ALL
|
||||||
.iter()
|
.iter()
|
||||||
.map(|mode| mode.label().to_string())
|
.map(|mode| mode.label().to_string())
|
||||||
|
|
@ -276,4 +273,3 @@ fn resolution_field(width: State<String>, height: State<String>) -> impl IntoEle
|
||||||
)
|
)
|
||||||
.into_element()
|
.into_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@ use freya::prelude::*;
|
||||||
|
|
||||||
pub use accounts::SettingsAccounts;
|
pub use accounts::SettingsAccounts;
|
||||||
pub use apis::SettingsApis;
|
pub use apis::SettingsApis;
|
||||||
|
pub use appearance::SettingsAppearance;
|
||||||
pub use changelog::SettingsChangelog;
|
pub use changelog::SettingsChangelog;
|
||||||
pub use developer::SettingsDeveloper;
|
pub use developer::SettingsDeveloper;
|
||||||
pub use appearance::SettingsAppearance;
|
|
||||||
pub use java::SettingsJava;
|
pub use java::SettingsJava;
|
||||||
pub use language::SettingsLanguage;
|
pub use language::SettingsLanguage;
|
||||||
pub use launcher::SettingsLauncher;
|
pub use launcher::SettingsLauncher;
|
||||||
|
|
|
||||||
|
|
@ -322,10 +322,7 @@ fn unused_cache_description(entry: &ReclaimableEntry) -> String {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn legacy_content_description(
|
fn legacy_content_description(content: &ReclaimableEntry, natives: &ReclaimableEntry) -> String {
|
||||||
content: &ReclaimableEntry,
|
|
||||||
natives: &ReclaimableEntry,
|
|
||||||
) -> String {
|
|
||||||
if content.is_empty() && natives.is_empty() {
|
if content.is_empty() && natives.is_empty() {
|
||||||
return "Nothing here - no old cluster content and no unused natives.".to_string();
|
return "Nothing here - no old cluster content and no unused natives.".to_string();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use freya::prelude::*;
|
||||||
use crate::components::ScrollArea;
|
use crate::components::ScrollArea;
|
||||||
use crate::hooks::{try_global_analytics, use_global_analytics};
|
use crate::hooks::{try_global_analytics, use_global_analytics};
|
||||||
use crate::theme::colors;
|
use crate::theme::colors;
|
||||||
use crate::ui::{centered_note};
|
use crate::ui::centered_note;
|
||||||
use crate::view::app::{analytics_body, analytics_placeholder};
|
use crate::view::app::{analytics_body, analytics_placeholder};
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,11 @@ fn raise_to_debug() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn restore_directives() {
|
fn restore_directives() {
|
||||||
let Some(previous) = PREVIOUS_DIRECTIVES.lock().expect("console directives").take() else {
|
let Some(previous) = PREVIOUS_DIRECTIVES
|
||||||
|
.lock()
|
||||||
|
.expect("console directives")
|
||||||
|
.take()
|
||||||
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use freya::prelude::*;
|
use freya::prelude::*;
|
||||||
use oneclient_core::clusters::Cluster;
|
|
||||||
use oneclient_content::packages::ProviderId;
|
use oneclient_content::packages::ProviderId;
|
||||||
|
use oneclient_core::clusters::Cluster;
|
||||||
use oneclient_core::{BundleArchive, BundleFile, BundleFileKind};
|
use oneclient_core::{BundleArchive, BundleFile, BundleFileKind};
|
||||||
|
|
||||||
use crate::components::ScrollArea;
|
use crate::components::ScrollArea;
|
||||||
use crate::hooks::{ClusterBundles, onboarding_bundles_items, package_meta_batch, query_error, query_is_loading, use_onboarding_bundles, use_onboarding_selection, use_package_meta_batch};
|
use crate::hooks::{
|
||||||
|
ClusterBundles, onboarding_bundles_items, package_meta_batch, query_error, query_is_loading,
|
||||||
|
use_onboarding_bundles, use_onboarding_selection, use_package_meta_batch,
|
||||||
|
};
|
||||||
|
|
||||||
type MetaMap = HashMap<String, oneclient_content::packages::CachedPackageMeta>;
|
type MetaMap = HashMap<String, oneclient_content::packages::CachedPackageMeta>;
|
||||||
use crate::routes::Route;
|
use crate::routes::Route;
|
||||||
|
|
@ -341,55 +344,50 @@ fn opt_in_card(
|
||||||
let yes_ids = all_ids.clone();
|
let yes_ids = all_ids.clone();
|
||||||
let no_bundle = bundle.clone();
|
let no_bundle = bundle.clone();
|
||||||
|
|
||||||
let mut card = rect()
|
let mut card = rect().vertical().width(Size::fill()).spacing(10.).child(
|
||||||
.vertical()
|
label()
|
||||||
.width(Size::fill())
|
.text(format!("Do you want to install {name} mods?"))
|
||||||
.spacing(10.)
|
.font_size(15.)
|
||||||
.child(
|
.font_weight(FontWeight::SEMI_BOLD)
|
||||||
label()
|
.color(colors::fg_primary()),
|
||||||
.text(format!("Do you want to install {name} mods?"))
|
);
|
||||||
.font_size(15.)
|
|
||||||
.font_weight(FontWeight::SEMI_BOLD)
|
|
||||||
.color(colors::fg_primary()),
|
|
||||||
);
|
|
||||||
|
|
||||||
card = card
|
card = card.child(
|
||||||
.child(
|
rect()
|
||||||
rect()
|
.horizontal()
|
||||||
.horizontal()
|
.width(Size::fill())
|
||||||
.width(Size::fill())
|
.spacing(8.)
|
||||||
.spacing(8.)
|
.content(Content::Flex)
|
||||||
.content(Content::Flex)
|
.child(
|
||||||
.child(
|
rect()
|
||||||
rect()
|
.width(Size::flex(1.0))
|
||||||
.width(Size::flex(1.0))
|
.height(Size::px(ANSWER_H))
|
||||||
.height(Size::px(ANSWER_H))
|
.child(choice_row_sized(
|
||||||
.child(choice_row_sized(
|
"Yes",
|
||||||
"Yes",
|
&format!("{} mods", yes_bundle.mod_count()),
|
||||||
&format!("{} mods", yes_bundle.mod_count()),
|
wanted,
|
||||||
wanted,
|
Size::fill(),
|
||||||
Size::fill(),
|
move |()| {
|
||||||
move |()| {
|
if wanted {
|
||||||
if wanted {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
set_bundle_versions(selected, user_touched, &yes_bundle, &yes_ids)
|
||||||
set_bundle_versions(selected, user_touched, &yes_bundle, &yes_ids)
|
},
|
||||||
},
|
)),
|
||||||
)),
|
)
|
||||||
)
|
.child(
|
||||||
.child(
|
rect()
|
||||||
rect()
|
.width(Size::flex(1.0))
|
||||||
.width(Size::flex(1.0))
|
.height(Size::px(ANSWER_H))
|
||||||
.height(Size::px(ANSWER_H))
|
.child(choice_row_sized(
|
||||||
.child(choice_row_sized(
|
"No",
|
||||||
"No",
|
"",
|
||||||
"",
|
!wanted,
|
||||||
!wanted,
|
Size::fill(),
|
||||||
Size::fill(),
|
move |()| set_bundle_versions(selected, user_touched, &no_bundle, &[]),
|
||||||
move |()| set_bundle_versions(selected, user_touched, &no_bundle, &[]),
|
)),
|
||||||
)),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
if wanted && name.eq_ignore_ascii_case(FPS_WARNING_BUNDLE) {
|
if wanted && name.eq_ignore_ascii_case(FPS_WARNING_BUNDLE) {
|
||||||
card = card.child(fps_warning_banner());
|
card = card.child(fps_warning_banner());
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ use std::time::{Duration, Instant};
|
||||||
use freya::animation::*;
|
use freya::animation::*;
|
||||||
use freya::prelude::*;
|
use freya::prelude::*;
|
||||||
use freya::router::RouterContext;
|
use freya::router::RouterContext;
|
||||||
use oneclient_events::{
|
|
||||||
EventBus, GroupedProgressEvent, GroupedProgressSession, ProgressEvent, TaskCategory,
|
|
||||||
};
|
|
||||||
use oneclient_core::{
|
use oneclient_core::{
|
||||||
BundleArchive, BundleFile, Cluster, ImportTarget, MigrationSource, SentryExclusion,
|
BundleArchive, BundleFile, Cluster, ImportTarget, MigrationSource, SentryExclusion,
|
||||||
VersionMetadata,
|
VersionMetadata,
|
||||||
};
|
};
|
||||||
use oneclient_db::models::OverrideType;
|
use oneclient_db::models::OverrideType;
|
||||||
|
use oneclient_events::{
|
||||||
|
EventBus, GroupedProgressEvent, GroupedProgressSession, ProgressEvent, TaskCategory,
|
||||||
|
};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
|
@ -502,12 +502,7 @@ fn build_plans(
|
||||||
let kept = kept_package_ids(cb.cluster.id, &cb.archives, selected);
|
let kept = kept_package_ids(cb.cluster.id, &cb.archives, selected);
|
||||||
let mut overrides = Vec::new();
|
let mut overrides = Vec::new();
|
||||||
for archive in &cb.archives {
|
for archive in &cb.archives {
|
||||||
overrides.extend(archive_overrides(
|
overrides.extend(archive_overrides(cb.cluster.id, archive, selected, &kept));
|
||||||
cb.cluster.id,
|
|
||||||
archive,
|
|
||||||
selected,
|
|
||||||
&kept,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
ClusterPlan {
|
ClusterPlan {
|
||||||
cluster_id: cb.cluster.id,
|
cluster_id: cb.cluster.id,
|
||||||
|
|
@ -693,7 +688,9 @@ fn run_install_batch(plans: Vec<ClusterPlan>, predownload: bool, handles: Instal
|
||||||
apply_grouped(&mut local, event);
|
apply_grouped(&mut local, event);
|
||||||
}
|
}
|
||||||
while let Ok(notification) = notif_rx.try_recv() {
|
while let Ok(notification) = notif_rx.try_recv() {
|
||||||
if let oneclient_events::Event::Progress(ProgressEvent::Grouped(event)) = notification {
|
if let oneclient_events::Event::Progress(ProgressEvent::Grouped(event)) =
|
||||||
|
notification
|
||||||
|
{
|
||||||
apply_grouped(&mut local, event);
|
apply_grouped(&mut local, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -799,15 +796,18 @@ async fn install_one(
|
||||||
"Saving your package choices...".to_string(),
|
"Saving your package choices...".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
oneclient_core::set_bundle_package_overrides(plan.cluster_id, &plan.overrides, &state.services.content())
|
oneclient_core::set_bundle_package_overrides(
|
||||||
.await?;
|
plan.cluster_id,
|
||||||
|
&plan.overrides,
|
||||||
|
&state.services.content(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
if !predownload {
|
if !predownload {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let session =
|
let session = GroupedProgressSession::start(events, format!("Downloading {}", plan.mc_version));
|
||||||
GroupedProgressSession::start(events, format!("Downloading {}", plan.mc_version));
|
|
||||||
|
|
||||||
let _ = ui_tx.send(InstallUiEvent::Activity(format!(
|
let _ = ui_tx.send(InstallUiEvent::Activity(format!(
|
||||||
"Installing mods & content for {}...",
|
"Installing mods & content for {}...",
|
||||||
|
|
|
||||||
|
|
@ -251,10 +251,7 @@ fn build_stats(view: StatsView) -> Option<Element> {
|
||||||
} else {
|
} else {
|
||||||
// Both readings come off the same smoothed rate so they always agree
|
// Both readings come off the same smoothed rate so they always agree
|
||||||
match transfer.and_then(|t| t.eta_secs) {
|
match transfer.and_then(|t| t.eta_secs) {
|
||||||
Some(remaining) => parts.push(format!(
|
Some(remaining) => parts.push(format!("~{} left", format_durations(remaining as i64))),
|
||||||
"~{} left",
|
|
||||||
format_durations(remaining as i64)
|
|
||||||
)),
|
|
||||||
None => parts.push("~- left".to_string()),
|
None => parts.push("~- left".to_string()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,7 @@ const ONBOARDING_TIPS_BACKUP: &[&str] = &[
|
||||||
"You can sleep through a thunderstorm, even during the day",
|
"You can sleep through a thunderstorm, even during the day",
|
||||||
];
|
];
|
||||||
|
|
||||||
const FUNFACTS_URL: &str =
|
const FUNFACTS_URL: &str = "https://data-v2.polyfrost.org/oneclient/funfacts.txt";
|
||||||
"https://data-v2.polyfrost.org/oneclient/funfacts.txt";
|
|
||||||
|
|
||||||
fn normalize_line_breaks(value: &str) -> String {
|
fn normalize_line_breaks(value: &str) -> String {
|
||||||
value.replace("/n", "\n").replace("\\n", "\n")
|
value.replace("/n", "\n").replace("\\n", "\n")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use oneclient_core::clusters::{Cluster, ClusterStage};
|
|
||||||
use oneclient_common::domain::{ContentType, GameLoader};
|
use oneclient_common::domain::{ContentType, GameLoader};
|
||||||
use oneclient_content::packages::types::ExternalFile;
|
use oneclient_content::packages::types::ExternalFile;
|
||||||
|
use oneclient_core::clusters::{Cluster, ClusterStage};
|
||||||
use oneclient_core::{Bundle, BundleArchive, BundleFile, BundleFileKind, BundleManifest};
|
use oneclient_core::{Bundle, BundleArchive, BundleFile, BundleFileKind, BundleManifest};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,11 @@ fn copying(plan: &RelocationPlan, copied: u64, total: u64) -> Element {
|
||||||
let total = if total > 0 { total } else { plan.bytes }.max(1);
|
let total = if total > 0 { total } else { plan.bytes }.max(1);
|
||||||
|
|
||||||
screen()
|
screen()
|
||||||
.child(heading(IconType::FolderDownload, "Moving your data folder", colors::fg_primary()))
|
.child(heading(
|
||||||
|
IconType::FolderDownload,
|
||||||
|
"Moving your data folder",
|
||||||
|
colors::fg_primary(),
|
||||||
|
))
|
||||||
.child(path_block("From", &plan.from))
|
.child(path_block("From", &plan.from))
|
||||||
.child(path_block("To", &plan.to))
|
.child(path_block("To", &plan.to))
|
||||||
.child(progress_track(
|
.child(progress_track(
|
||||||
|
|
@ -90,7 +94,11 @@ fn moved(outcome: &RelocationOutcome, actions: Actions) -> Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
screen()
|
screen()
|
||||||
.child(heading(IconType::FolderCheck, "Game data moved", colors::brand()))
|
.child(heading(
|
||||||
|
IconType::FolderCheck,
|
||||||
|
"Game data moved",
|
||||||
|
colors::brand(),
|
||||||
|
))
|
||||||
.child(note(body, colors::brand()))
|
.child(note(body, colors::brand()))
|
||||||
.child(
|
.child(
|
||||||
label()
|
label()
|
||||||
|
|
@ -103,26 +111,28 @@ fn moved(outcome: &RelocationOutcome, actions: Actions) -> Element {
|
||||||
.color(colors::fg_secondary()),
|
.color(colors::fg_secondary()),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
buttons()
|
buttons().child(back_button(actions)).child(
|
||||||
.child(back_button(actions))
|
Button::new()
|
||||||
.child(
|
.primary()
|
||||||
Button::new()
|
.on_press(|_| {
|
||||||
.primary()
|
let platform = Platform::get();
|
||||||
.on_press(|_| {
|
Platform::get().with_window(Platform::window_id(), move |window| {
|
||||||
let platform = Platform::get();
|
platform.close_window(window.id());
|
||||||
Platform::get().with_window(Platform::window_id(), move |window| {
|
});
|
||||||
platform.close_window(window.id());
|
})
|
||||||
});
|
.text("Quit OneClient"),
|
||||||
})
|
),
|
||||||
.text("Quit OneClient"),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.into_element()
|
.into_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn failed(message: String, actions: Actions) -> Element {
|
fn failed(message: String, actions: Actions) -> Element {
|
||||||
screen()
|
screen()
|
||||||
.child(heading(IconType::AlertTriangle, "The move didn't finish", colors::danger()))
|
.child(heading(
|
||||||
|
IconType::AlertTriangle,
|
||||||
|
"The move didn't finish",
|
||||||
|
colors::danger(),
|
||||||
|
))
|
||||||
.child(note(message, colors::danger()))
|
.child(note(message, colors::danger()))
|
||||||
.child(
|
.child(
|
||||||
label()
|
label()
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@ use freya::{prelude::*, router::*};
|
||||||
|
|
||||||
use crate::AppAssets;
|
use crate::AppAssets;
|
||||||
use crate::Route;
|
use crate::Route;
|
||||||
use crate::hooks::{
|
|
||||||
terms_document, terms_is_loading, use_launcher, use_notifications_snapshot, use_settings_snapshot,
|
|
||||||
use_splash, use_terms,
|
|
||||||
};
|
|
||||||
use crate::hooks::use_dispatch;
|
|
||||||
use crate::components::{Button, progress_track};
|
use crate::components::{Button, progress_track};
|
||||||
|
use crate::hooks::use_dispatch;
|
||||||
|
use crate::hooks::{
|
||||||
|
terms_document, terms_is_loading, use_launcher, use_notifications_snapshot,
|
||||||
|
use_settings_snapshot, use_splash, use_terms,
|
||||||
|
};
|
||||||
use crate::theme::colors;
|
use crate::theme::colors;
|
||||||
|
|
||||||
/// Startup is a sequence of boolean gates, not measurable work, so the bar steps
|
/// Startup is a sequence of boolean gates, not measurable work, so the bar steps
|
||||||
|
|
@ -227,7 +227,9 @@ impl Component for Startup {
|
||||||
if is_error {
|
if is_error {
|
||||||
content = content
|
content = content
|
||||||
.child(rect().height(Size::px(6.)))
|
.child(rect().height(Size::px(6.)))
|
||||||
.child(RecoveryActions { snapshots: launcher.snapshots })
|
.child(RecoveryActions {
|
||||||
|
snapshots: launcher.snapshots,
|
||||||
|
})
|
||||||
.child(
|
.child(
|
||||||
label()
|
label()
|
||||||
.text(format!(
|
.text(format!(
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
use oneclient_net::{NetworkFailure, classify_network_failure};
|
use oneclient_net::{NetworkFailure, classify_network_failure};
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
use super::error::{friendly_xbox_error, MinecraftAuthError, MinecraftAuthStep};
|
use super::error::{MinecraftAuthError, MinecraftAuthStep, friendly_xbox_error};
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize)]
|
#[derive(Debug, Clone, serde::Serialize)]
|
||||||
pub struct AuthErrorGuidance {
|
pub struct AuthErrorGuidance {
|
||||||
|
|
@ -298,7 +298,9 @@ fn xerr(code: u64) -> MinecraftAuthError {
|
||||||
MinecraftAuthError::XboxError {
|
MinecraftAuthError::XboxError {
|
||||||
step: MinecraftAuthStep::XstsAuthorize,
|
step: MinecraftAuthStep::XstsAuthorize,
|
||||||
error_code: code,
|
error_code: code,
|
||||||
message: friendly_xbox_error(code).unwrap_or("Xbox rejected sign-in.").to_string(),
|
message: friendly_xbox_error(code)
|
||||||
|
.unwrap_or("Xbox rejected sign-in.")
|
||||||
|
.to_string(),
|
||||||
redirect: None,
|
redirect: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,10 @@ pub enum MinecraftAuthError {
|
||||||
},
|
},
|
||||||
// Rendered via `error_chain` reqwest's `Display` names only the URL and the
|
// Rendered via `error_chain` reqwest's `Display` names only the URL and the
|
||||||
// cause we need (certificate handshake DNS refused) is under it
|
// cause we need (certificate handshake DNS refused) is under it
|
||||||
#[error("failed to request using HTTP during MSA step {step:?}: {}", oneclient_net::error_chain(source))]
|
#[error(
|
||||||
|
"failed to request using HTTP during MSA step {step:?}: {}",
|
||||||
|
oneclient_net::error_chain(source)
|
||||||
|
)]
|
||||||
RequestError {
|
RequestError {
|
||||||
step: MinecraftAuthStep,
|
step: MinecraftAuthStep,
|
||||||
#[source]
|
#[source]
|
||||||
|
|
@ -79,12 +82,24 @@ pub enum MinecraftAuthError {
|
||||||
pub fn friendly_xbox_error(code: u64) -> Option<&'static str> {
|
pub fn friendly_xbox_error(code: u64) -> Option<&'static str> {
|
||||||
Some(match code {
|
Some(match code {
|
||||||
2_148_916_227 => "This account has been banned or suspended from Xbox.",
|
2_148_916_227 => "This account has been banned or suspended from Xbox.",
|
||||||
2_148_916_229 => "This account is a child account that must be added to a Family group by an adult before signing in.",
|
2_148_916_229 => {
|
||||||
2_148_916_233 => "This Microsoft account does not have an Xbox profile yet. Create one at xbox.com, then try again.",
|
"This account is a child account that must be added to a Family group by an adult before signing in."
|
||||||
2_148_916_234 => "This account has not accepted the Xbox Terms of Service. Sign in at xbox.com to accept them first.",
|
}
|
||||||
2_148_916_235 => "Xbox Live is not available in your country or region, so sign-in is blocked.",
|
2_148_916_233 => {
|
||||||
2_148_916_236 | 2_148_916_237 => "This account requires adult verification (South Korea) before it can sign in.",
|
"This Microsoft account does not have an Xbox profile yet. Create one at xbox.com, then try again."
|
||||||
2_148_916_238 => "This is a child account. An adult must add it to a Microsoft Family group before it can sign in.",
|
}
|
||||||
|
2_148_916_234 => {
|
||||||
|
"This account has not accepted the Xbox Terms of Service. Sign in at xbox.com to accept them first."
|
||||||
|
}
|
||||||
|
2_148_916_235 => {
|
||||||
|
"Xbox Live is not available in your country or region, so sign-in is blocked."
|
||||||
|
}
|
||||||
|
2_148_916_236 | 2_148_916_237 => {
|
||||||
|
"This account requires adult verification (South Korea) before it can sign in."
|
||||||
|
}
|
||||||
|
2_148_916_238 => {
|
||||||
|
"This is a child account. An adult must add it to a Microsoft Family group before it can sign in."
|
||||||
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue