Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions packages/oneclient_app/src/components/notifications.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
ui::{divider, relative_time},
components::{Button, ButtonVariant, Icon, IconType, OverlayPopup, ScrollArea},
hooks::{use_dispatch, use_notifications_snapshot},
notifications::{InboxEntry, NotificationActionKind},
notifications::{InboxEntry, NotificationActionKind, NotificationState},
theme::colors,
transfer::TransferStats,
utils::{format_duration_hms, format_size},
Expand Down Expand Up @@ -39,7 +39,13 @@ struct NotificationPanel;

impl Component for NotificationPanel {
fn render(&self) -> impl IntoElement {
let inbox = use_notifications_snapshot().inbox;
// Transient notifications ride the same inbox so their toast can find
// them, but they are not what this panel is for.
let inbox: Vec<InboxEntry> = NotificationState::center_entries(
&use_notifications_snapshot().inbox,
)
.cloned()
.collect();

let intro = use_animation(|conf| {
conf.on_creation(OnCreation::Run);
Expand Down Expand Up @@ -491,7 +497,8 @@ struct Footer;
impl Component for Footer {
fn render(&self) -> impl IntoElement {
let dispatch = use_dispatch();
let is_empty = use_notifications_snapshot().inbox.is_empty();
let is_empty =
NotificationState::center_entries(&use_notifications_snapshot().inbox).count() == 0;

rect()
.horizontal()
Expand Down
1 change: 1 addition & 0 deletions packages/oneclient_app/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ pub fn report_startup_failure(
title: "Launcher failed to start".into(),
body: message,
level: oneclient_events::Level::Error,
persistence: oneclient_events::Persistence::Persistent,
},
)),
);
Expand Down
41 changes: 39 additions & 2 deletions packages/oneclient_app/src/hooks/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use oneclient_common::domain::{ContentType, ProviderId};
use oneclient_core::settings::LauncherSettings;
use oneclient_core::settings::store::{save_global_profile, save_settings_and_apply};
use oneclient_db::models::ClusterId;
use oneclient_events::{Answer, Level};
use oneclient_events::{Answer, Level, Persistence};
use tokio::sync::mpsc;

use crate::components::IconType;
Expand Down Expand Up @@ -613,7 +613,9 @@ impl Actions {
icon: None,
progress: None,
actions: Vec::new(),
persistence: Persistence::Transient,
},
persistence: None,
}
}

Expand Down Expand Up @@ -775,6 +777,9 @@ impl Actions {
icon: Some(IconType::Download01),
progress: None,
actions: Vec::new(),
// The package is in the list the user is looking at; the
// list is the record, not the notification.
persistence: Persistence::Transient,
},
Err(err) => NotificationSpec {
title: "Install failed".to_string(),
Expand All @@ -783,6 +788,7 @@ impl Actions {
icon: None,
progress: None,
actions: Vec::new(),
persistence: Persistence::Persistent,
},
};

Expand Down Expand Up @@ -1179,6 +1185,9 @@ impl Actions {
icon: Some(IconType::DownloadCloud02),
progress: None,
actions: Vec::new(),
// Applied automatically on the way into the game, so the user is
// told what changed at the worst possible moment to read it.
persistence: Persistence::Persistent,
});

self.with_engine(|app| {
Expand Down Expand Up @@ -1243,6 +1252,8 @@ impl Actions {
icon: Some(IconType::DownloadCloud02),
progress: None,
actions: Vec::new(),
// The user pressed Update on this row and watched it go.
persistence: Persistence::Transient,
},
Err(err) => NotificationSpec {
title: "Update failed".to_string(),
Expand All @@ -1251,6 +1262,7 @@ impl Actions {
icon: None,
progress: None,
actions: Vec::new(),
persistence: Persistence::Persistent,
},
};

Expand Down Expand Up @@ -1386,6 +1398,10 @@ async fn repair_and_relaunch(
events
.notify("Repair complete")
.body(report.summary())
// The launcher repaired the install on its own initiative, between the
// user pressing Play and the game appearing; the summary is the only
// account of what it changed.
.persistent()
.send();

if let Err(err) = oneclient_core::launch_cluster(state, cluster_id, account, true).await {
Expand All @@ -1394,10 +1410,16 @@ async fn repair_and_relaunch(
}
}

/// The front-end twin of [`oneclient_events::NotificationBuilder`], for
/// notifications the UI raises itself. Same persistence rules, so a call site
/// reads identically whichever side of the bus it lives on.
#[must_use = "the notification is not raised until `.send()` is called"]
pub struct NotificationBuilder {
actions: Actions,
spec: NotificationSpec,
/// Explicit choice; resolved against the level in `send`, so the order the
/// builder is called in cannot change where the notification lands.
persistence: Option<Persistence>,
}

impl NotificationBuilder {
Expand All @@ -1419,6 +1441,18 @@ impl NotificationBuilder {
self.level(Level::Error)
}

/// Files this notification in the notification center.
pub fn persistent(mut self) -> Self {
self.persistence = Some(Persistence::Persistent);
self
}

/// Shows this notification and forgets it.
pub fn transient(mut self) -> Self {
self.persistence = Some(Persistence::Transient);
self
}

pub fn icon(mut self, icon: IconType) -> Self {
self.spec.icon = Some(icon);
self
Expand All @@ -1439,7 +1473,10 @@ impl NotificationBuilder {
self
}

pub fn send(self) {
pub fn send(mut self) {
self.spec.persistence = self
.persistence
.unwrap_or_else(|| Persistence::for_level(self.spec.level));
self.actions.push_notification(self.spec);
}
}
Expand Down
8 changes: 7 additions & 1 deletion packages/oneclient_app/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::sync::Arc;
use oneclient_core::LauncherState;

use oneclient_content::packages::PackageStore;
use oneclient_events::Level;
use oneclient_events::{Level, Persistence};

use crate::components::IconType;
use crate::notifications::{
Expand Down Expand Up @@ -137,6 +137,9 @@ pub async fn cluster_update_notification(
label: "View changes".to_string(),
kind: NotificationActionKind::OpenClusterUpdate(vec![summary]),
}],
// Packages moved under the user without them asking, and "View changes"
// is only worth offering for as long as it is still reachable.
persistence: Persistence::Persistent,
})
}

Expand Down Expand Up @@ -179,6 +182,9 @@ pub async fn combined_cluster_update_spec(
label: "View changes".to_string(),
kind: NotificationActionKind::OpenClusterUpdate(summaries),
}],
// Same as the single-cluster case: a background sync the user did not
// ask for, with changes they may want to look at afterwards.
persistence: Persistence::Persistent,
})
}

Expand Down
3 changes: 3 additions & 0 deletions packages/oneclient_app/src/layout/app_shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ fn copy_error_button(message: &str, dispatch: crate::Actions) -> impl IntoElemen
.notify("Copy failed")
.body("Could not copy the error to the clipboard.")
.error()
// An error, but one the user answers by pressing Copy
// again; nothing survives it that is worth reviewing.
.transient()
.send();
} else {
dispatch
Expand Down
1 change: 1 addition & 0 deletions packages/oneclient_app/src/layout/settings_shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ impl Component for SidebarInfo {
.notify("Copy failed")
.body("Could not copy system information to the clipboard.")
.error()
.transient()
.send();
} else {
dispatch
Expand Down
Loading