Limit status errors to related control popups
This commit is contained in:
1 parent
7a9fd5c864
commit
5250eeca7b
3 files changed
+79
-12
No files matched your search
@@ -165,7 +165,7 @@ struct FereseShell {
|
|||||||
status: status::Snapshot,
|
status: status::Snapshot,
|
||||||
recorder: recording::Recorder,
|
recorder: recording::Recorder,
|
||||||
calendar_offset: i32,
|
calendar_offset: i32,
|
||||||
status_error: Option<String>,
|
status_error: Option<status::ActionError>,
|
||||||
theme_error: Option<String>,
|
theme_error: Option<String>,
|
||||||
menu: Option<status_ui::OpenMenu>,
|
menu: Option<status_ui::OpenMenu>,
|
||||||
system_modal: Option<system_modal::SystemModal>,
|
system_modal: Option<system_modal::SystemModal>,
|
||||||
|
|||||||
@@ -84,11 +84,17 @@ impl Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ActionError {
|
||||||
|
pub action: Action,
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Update {
|
pub struct Update {
|
||||||
pub snapshot: Snapshot,
|
pub snapshot: Snapshot,
|
||||||
pub generation: u64,
|
pub generation: u64,
|
||||||
pub error: Option<String>,
|
pub error: Option<ActionError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Service {
|
pub struct Service {
|
||||||
@@ -123,7 +129,7 @@ impl Updates {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type PollState = (Mutex<(u64, bool, Option<String>, bool)>, Condvar);
|
type PollState = (Mutex<(u64, bool, Option<ActionError>, bool)>, Condvar);
|
||||||
|
|
||||||
fn wait_for_poll(shared: &PollState) -> Option<u64> {
|
fn wait_for_poll(shared: &PollState) -> Option<u64> {
|
||||||
let state = shared
|
let state = shared
|
||||||
@@ -141,7 +147,7 @@ impl Service {
|
|||||||
let (updates, rx) = tokio::sync::watch::channel(None);
|
let (updates, rx) = tokio::sync::watch::channel(None);
|
||||||
// Polling and writes have separate workers: a missing D-Bus service must
|
// Polling and writes have separate workers: a missing D-Bus service must
|
||||||
// never hold up volume/brightness changes. Publish only coherent polls.
|
// never hold up volume/brightness changes. Publish only coherent polls.
|
||||||
let shared = Arc::new((Mutex::new((0u64, false, None::<String>, false)), Condvar::new()));
|
let shared = Arc::new((Mutex::new((0u64, false, None::<ActionError>, false)), Condvar::new()));
|
||||||
let polling = shared.clone();
|
let polling = shared.clone();
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
@@ -190,7 +196,9 @@ impl Service {
|
|||||||
state.1 = true;
|
state.1 = true;
|
||||||
}
|
}
|
||||||
let settings = settings.lock().unwrap().clone();
|
let settings = settings.lock().unwrap().clone();
|
||||||
let error = execute(&action, settings.as_deref()).err();
|
let error = execute(&action, settings.as_deref())
|
||||||
|
.err()
|
||||||
|
.map(|message| ActionError { action, message });
|
||||||
let mut state = shared.0.lock().unwrap();
|
let mut state = shared.0.lock().unwrap();
|
||||||
state.1 = false;
|
state.1 = false;
|
||||||
state.2 = error;
|
state.2 = error;
|
||||||
@@ -210,11 +218,13 @@ impl Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send(&mut self, action: Action) -> Result<(), String> {
|
pub fn send(&mut self, action: Action) -> Result<(), ActionError> {
|
||||||
let generation = self.generation + 1;
|
let generation = self.generation + 1;
|
||||||
self.tx
|
let error_action = action.clone();
|
||||||
.try_send((generation, action))
|
self.tx.try_send((generation, action)).map_err(|_| ActionError {
|
||||||
.map_err(|_| "Controls are busy; please try again".to_owned())?;
|
action: error_action,
|
||||||
|
message: "Controls are busy; please try again".to_owned(),
|
||||||
|
})?;
|
||||||
self.generation = generation;
|
self.generation = generation;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -745,7 +755,10 @@ mod tests {
|
|||||||
..Snapshot::default()
|
..Snapshot::default()
|
||||||
},
|
},
|
||||||
generation,
|
generation,
|
||||||
error: Some("service unavailable".into()),
|
error: Some(ActionError {
|
||||||
|
action: Action::Bluetooth(true),
|
||||||
|
message: "service unavailable".into(),
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
let (sender, receiver) = tokio::sync::watch::channel(None);
|
let (sender, receiver) = tokio::sync::watch::channel(None);
|
||||||
@@ -763,7 +776,9 @@ mod tests {
|
|||||||
};
|
};
|
||||||
assert_eq!(update.generation, 2);
|
assert_eq!(update.generation, 2);
|
||||||
assert_eq!(update.snapshot.brightness, Some(42));
|
assert_eq!(update.snapshot.brightness, Some(42));
|
||||||
assert_eq!(update.error.as_deref(), Some("service unavailable"));
|
let error = update.error.as_ref().unwrap();
|
||||||
|
assert_eq!(error.message, "service unavailable");
|
||||||
|
assert!(matches!(error.action, Action::Bluetooth(true)));
|
||||||
assert!(stream.as_mut().poll_next(&mut context).is_pending());
|
assert!(stream.as_mut().poll_next(&mut context).is_pending());
|
||||||
let before = wakes.0.load(Ordering::Relaxed);
|
let before = wakes.0.load(Ordering::Relaxed);
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
|||||||
@@ -24,6 +24,19 @@ pub enum Menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Menu {
|
impl Menu {
|
||||||
|
fn action_error(self, error: &status::ActionError) -> Option<&str> {
|
||||||
|
let relevant = self == Self::System
|
||||||
|
|| matches!(
|
||||||
|
(self, &error.action),
|
||||||
|
(Self::Network, Action::Wifi(_))
|
||||||
|
| (Self::Bluetooth, Action::Bluetooth(_))
|
||||||
|
| (Self::Audio, Action::Volume(_) | Action::Mute(_))
|
||||||
|
| (Self::Battery, Action::Brightness(_) | Action::PowerProfile(_))
|
||||||
|
| (Self::Notifications, Action::Dnd(_) | Action::Notifications)
|
||||||
|
);
|
||||||
|
relevant.then_some(error.message.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
fn width(self) -> f32 {
|
fn width(self) -> f32 {
|
||||||
match self {
|
match self {
|
||||||
Self::System => 360.0,
|
Self::System => 360.0,
|
||||||
@@ -818,7 +831,11 @@ impl FereseShell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(error) = &self.status_error {
|
if let Some(error) = self
|
||||||
|
.status_error
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|error| menu.kind.action_error(error))
|
||||||
|
{
|
||||||
rows = rows.push(text(error).size(12).class(theme::Text::Color(Color {
|
rows = rows.push(text(error).size(12).class(theme::Text::Color(Color {
|
||||||
a: p,
|
a: p,
|
||||||
..Color::from_rgb8(230, 172, 90)
|
..Color::from_rgb8(230, 172, 90)
|
||||||
@@ -1271,6 +1288,41 @@ fn status_icon(kind: Menu, s: &Snapshot) -> (&'static [u8], bool) {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn failed_controls_only_show_errors_in_related_popups() {
|
||||||
|
for (action, relevant) in [
|
||||||
|
(Action::Bluetooth(true), Menu::Bluetooth),
|
||||||
|
(Action::Wifi(true), Menu::Network),
|
||||||
|
(Action::Volume(50), Menu::Audio),
|
||||||
|
(Action::Mute(true), Menu::Audio),
|
||||||
|
(Action::Brightness(50), Menu::Battery),
|
||||||
|
(Action::PowerProfile("balanced"), Menu::Battery),
|
||||||
|
(Action::Dnd(true), Menu::Notifications),
|
||||||
|
] {
|
||||||
|
let error = status::ActionError {
|
||||||
|
action,
|
||||||
|
message: "control failed".into(),
|
||||||
|
};
|
||||||
|
for menu in [
|
||||||
|
Menu::Network,
|
||||||
|
Menu::Bluetooth,
|
||||||
|
Menu::Audio,
|
||||||
|
Menu::Battery,
|
||||||
|
Menu::Calendar,
|
||||||
|
Menu::Recording,
|
||||||
|
Menu::Notifications,
|
||||||
|
Menu::System,
|
||||||
|
] {
|
||||||
|
assert_eq!(
|
||||||
|
menu.action_error(&error),
|
||||||
|
(menu == relevant || menu == Menu::System).then_some("control failed"),
|
||||||
|
"{menu:?} showed the wrong control error: {:?}",
|
||||||
|
error.action
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn connection_captions_fit_one_short_line() {
|
fn connection_captions_fit_one_short_line() {
|
||||||
assert_eq!(super::connection_caption("88%"), "88%");
|
assert_eq!(super::connection_caption("88%"), "88%");
|
||||||
|
|||||||
Reference in new issue
Block a user