feat: added progress bar on first launch screen; changed progress bar to reusable component (there were 4 duplicates) (#740)
This commit is contained in:
parent
e659ee122a
commit
5cac70b026
6 changed files with 60 additions and 50 deletions
|
|
@ -24,6 +24,7 @@ mod package_row;
|
||||||
mod package_update_popup;
|
mod package_update_popup;
|
||||||
mod pagination;
|
mod pagination;
|
||||||
mod player_model;
|
mod player_model;
|
||||||
|
mod progress_track;
|
||||||
mod recents_row;
|
mod recents_row;
|
||||||
mod screenshot_viewer;
|
mod screenshot_viewer;
|
||||||
mod scrollview;
|
mod scrollview;
|
||||||
|
|
@ -66,6 +67,7 @@ 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;
|
||||||
|
pub use progress_track::progress_track;
|
||||||
pub use recents_row::RecentsRow;
|
pub use recents_row::RecentsRow;
|
||||||
pub use screenshot_viewer::ScreenshotViewer;
|
pub use screenshot_viewer::ScreenshotViewer;
|
||||||
pub(crate) use scrollview::corrected_scroll;
|
pub(crate) use scrollview::corrected_scroll;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use oneclient_events::Level;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ui::{divider, relative_time},
|
ui::{divider, relative_time},
|
||||||
components::{Button, ButtonVariant, Icon, IconType, OverlayPopup, ScrollArea},
|
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,
|
||||||
|
|
@ -382,18 +382,12 @@ fn row_body(entry: &InboxEntry, dispatch: crate::Actions) -> impl IntoElement {
|
||||||
fn row_extra(entry: &InboxEntry, dispatch: crate::Actions) -> impl IntoElement {
|
fn row_extra(entry: &InboxEntry, dispatch: crate::Actions) -> impl IntoElement {
|
||||||
if let Some((current, total)) = entry.progress.filter(|_| entry.is_loading) {
|
if let Some((current, total)) = entry.progress.filter(|_| entry.is_loading) {
|
||||||
let frac = (current as f32 / total.max(1) as f32).clamp(0.0, 1.0);
|
let frac = (current as f32 / total.max(1) as f32).clamp(0.0, 1.0);
|
||||||
let bar = rect()
|
let bar = progress_track(
|
||||||
.width(Size::fill())
|
frac * 100.0,
|
||||||
.height(Size::px(6.))
|
6.,
|
||||||
.corner_radius(CornerRadius::new_all(50.))
|
colors::brand(),
|
||||||
.background(colors::brand().with_a(128))
|
colors::brand().with_a(128),
|
||||||
.child(
|
);
|
||||||
rect()
|
|
||||||
.width(Size::percent(frac * 100.0))
|
|
||||||
.height(Size::px(6.))
|
|
||||||
.corner_radius(CornerRadius::new_all(50.))
|
|
||||||
.background(colors::brand()),
|
|
||||||
);
|
|
||||||
|
|
||||||
return rect()
|
return rect()
|
||||||
.vertical()
|
.vertical()
|
||||||
|
|
|
||||||
20
packages/oneclient_app/src/components/progress_track.rs
Normal file
20
packages/oneclient_app/src/components/progress_track.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
use freya::prelude::*;
|
||||||
|
|
||||||
|
/// The app's one progress meter: a pill-shaped track with a filled portion.
|
||||||
|
///
|
||||||
|
/// `pct` is 0-100 and is clamped here, so callers can hand over a raw ratio
|
||||||
|
/// without guarding against a denominator that grew mid-flight.
|
||||||
|
pub fn progress_track(pct: f32, height: f32, fill: Color, bg: Color) -> Rect {
|
||||||
|
rect()
|
||||||
|
.width(Size::fill())
|
||||||
|
.height(Size::px(height))
|
||||||
|
.corner_radius(CornerRadius::new_all(height / 2.))
|
||||||
|
.background(bg)
|
||||||
|
.child(
|
||||||
|
rect()
|
||||||
|
.width(Size::percent(pct.clamp(0.0, 100.0)))
|
||||||
|
.height(Size::fill())
|
||||||
|
.corner_radius(CornerRadius::new_all(height / 2.))
|
||||||
|
.background(fill),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ use oneclient_events::Level;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ui::{divider},
|
ui::{divider},
|
||||||
components::{Button, ButtonSize, Icon, IconType},
|
components::{Button, ButtonSize, Icon, IconType, progress_track},
|
||||||
hooks::{use_dispatch, use_notifications_snapshot},
|
hooks::{use_dispatch, use_notifications_snapshot},
|
||||||
notifications::{InboxEntry, NotificationActionKind},
|
notifications::{InboxEntry, NotificationActionKind},
|
||||||
theme::colors,
|
theme::colors,
|
||||||
|
|
@ -440,19 +440,13 @@ fn progress_card(entry: &InboxEntry) -> Rect {
|
||||||
.color(colors::fg_secondary()),
|
.color(colors::fg_secondary()),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
rect()
|
progress_track(
|
||||||
.width(Size::fill())
|
frac * 100.0,
|
||||||
.height(Size::px(6.))
|
6.,
|
||||||
.margin(Gaps::new(8., 0., 0., 0.))
|
colors::brand(),
|
||||||
.corner_radius(CornerRadius::new_all(50.))
|
colors::brand().with_a(128),
|
||||||
.background(colors::brand().with_a(128))
|
)
|
||||||
.child(
|
.margin(Gaps::new(8., 0., 0., 0.)),
|
||||||
rect()
|
|
||||||
.width(Size::percent(frac * 100.0))
|
|
||||||
.height(Size::px(6.))
|
|
||||||
.corner_radius(CornerRadius::new_all(50.))
|
|
||||||
.background(colors::brand()),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::components::Button;
|
use crate::components::{Button, progress_track};
|
||||||
use crate::theme::colors;
|
use crate::theme::colors;
|
||||||
use crate::ui::border_all_color;
|
use crate::ui::border_all_color;
|
||||||
use crate::utils::{format_duration_hms, format_size};
|
use crate::utils::{format_duration_hms, format_size};
|
||||||
|
|
@ -389,19 +389,3 @@ impl Component for ActivityIndicator {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn progress_track(pct: f32, height: f32, fill: Color, bg: Color) -> impl IntoElement {
|
|
||||||
rect()
|
|
||||||
.width(Size::fill())
|
|
||||||
.height(Size::px(height))
|
|
||||||
.corner_radius(CornerRadius::new_all(height / 2.))
|
|
||||||
.background(bg)
|
|
||||||
.child(
|
|
||||||
rect()
|
|
||||||
.width(Size::percent(pct.clamp(0.0, 100.0)))
|
|
||||||
.height(Size::fill())
|
|
||||||
.corner_radius(CornerRadius::new_all(height / 2.))
|
|
||||||
.background(fill),
|
|
||||||
)
|
|
||||||
.into_element()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,13 @@ use crate::hooks::{
|
||||||
use_splash, use_terms,
|
use_splash, use_terms,
|
||||||
};
|
};
|
||||||
use crate::hooks::use_dispatch;
|
use crate::hooks::use_dispatch;
|
||||||
use crate::components::Button;
|
use crate::components::{Button, progress_track};
|
||||||
use crate::theme::colors;
|
use crate::theme::colors;
|
||||||
|
|
||||||
|
/// Startup is a sequence of boolean gates, not measurable work, so the bar steps
|
||||||
|
/// once per gate the launcher clears instead of tracking bytes
|
||||||
|
const STARTUP_STEPS: u8 = 3;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
struct RecoveryActions {
|
struct RecoveryActions {
|
||||||
snapshots: usize,
|
snapshots: usize,
|
||||||
|
|
@ -124,11 +128,13 @@ impl Component for Startup {
|
||||||
|
|
||||||
let active = notifications.inbox.iter().find(|entry| entry.is_loading);
|
let active = notifications.inbox.iter().find(|entry| entry.is_loading);
|
||||||
|
|
||||||
let (message, detail): (String, Option<String>) =
|
let (message, detail, step): (String, Option<String>, u8) =
|
||||||
if let Some(err) = launcher.error.as_deref() {
|
if let Some(err) = launcher.error.as_deref() {
|
||||||
|
let step = if launcher.ready { 2 } else { 1 };
|
||||||
(
|
(
|
||||||
"Couldn't start OneClient".to_string(),
|
"Couldn't start OneClient".to_string(),
|
||||||
Some(err.to_string()),
|
Some(err.to_string()),
|
||||||
|
step,
|
||||||
)
|
)
|
||||||
} else if let Some(entry) = active {
|
} else if let Some(entry) = active {
|
||||||
let detail = entry
|
let detail = entry
|
||||||
|
|
@ -137,11 +143,11 @@ impl Component for Startup {
|
||||||
.find(|t| t.total == 0 || t.current < t.total)
|
.find(|t| t.total == 0 || t.current < t.total)
|
||||||
.map(|t| t.label.clone())
|
.map(|t| t.label.clone())
|
||||||
.or_else(|| entry.progress.map(|(c, t)| format!("{c} / {t}")));
|
.or_else(|| entry.progress.map(|(c, t)| format!("{c} / {t}")));
|
||||||
(entry.title.clone(), detail)
|
(entry.title.clone(), detail, 3)
|
||||||
} else if launcher.ready {
|
} else if launcher.ready {
|
||||||
("Fetching versions and bundles...".to_string(), None)
|
("Fetching versions and bundles...".to_string(), None, 2)
|
||||||
} else {
|
} else {
|
||||||
("Starting OneClient...".to_string(), None)
|
("Starting OneClient...".to_string(), None, 1)
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_error = launcher.error.is_some();
|
let is_error = launcher.error.is_some();
|
||||||
|
|
@ -156,6 +162,15 @@ impl Component for Startup {
|
||||||
colors::brand()
|
colors::brand()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let filled = f32::from(step) / f32::from(STARTUP_STEPS + 1) * 100.;
|
||||||
|
|
||||||
|
let progress = rect().width(Size::px(288.)).child(progress_track(
|
||||||
|
filled,
|
||||||
|
4.,
|
||||||
|
dot_color,
|
||||||
|
colors::fg_primary().with_a(30),
|
||||||
|
));
|
||||||
|
|
||||||
let loading_row = rect()
|
let loading_row = rect()
|
||||||
.horizontal()
|
.horizontal()
|
||||||
.cross_align(Alignment::Center)
|
.cross_align(Alignment::Center)
|
||||||
|
|
@ -192,7 +207,8 @@ impl Component for Startup {
|
||||||
.color(colors::fg_primary().with_a(140)),
|
.color(colors::fg_primary().with_a(140)),
|
||||||
)
|
)
|
||||||
.child(rect().height(Size::px(14.)))
|
.child(rect().height(Size::px(14.)))
|
||||||
.child(loading_row);
|
.child(loading_row)
|
||||||
|
.child(progress);
|
||||||
|
|
||||||
if let Some(detail) = detail {
|
if let Some(detail) = detail {
|
||||||
content = content.child(
|
content = content.child(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue