fix(time): Timezone stream spams updates

This commit is contained in:
Joshua Megnauth 2024-08-20 12:20:47 +00:00 committed by GitHub
parent 323e8a55b2
commit 0d0a13a062
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -9,14 +9,13 @@ use cosmic::{
applet::{cosmic_panel_config::PanelAnchor, menu_button, padded_control}, applet::{cosmic_panel_config::PanelAnchor, menu_button, padded_control},
cctk::sctk::reexports::calloop, cctk::sctk::reexports::calloop,
iced::{ iced::{
futures::{FutureExt, TryFutureExt}, futures::{channel::mpsc, SinkExt, StreamExt, TryFutureExt},
subscription, subscription,
wayland::popup::{destroy_popup, get_popup}, wayland::popup::{destroy_popup, get_popup},
widget::{column, row, text, vertical_space}, widget::{column, row, text, vertical_space},
window, Alignment, Length, Rectangle, Subscription, window, Alignment, Length, Rectangle, Subscription,
}, },
iced_core::alignment::{Horizontal, Vertical}, iced_core::alignment::{Horizontal, Vertical},
iced_runtime::command,
iced_style::application, iced_style::application,
iced_widget::{horizontal_rule, Column}, iced_widget::{horizontal_rule, Column},
widget::{ widget::{
@ -26,7 +25,6 @@ use cosmic::{
Command, Element, Theme, Command, Element, Theme,
}; };
use timedate_zbus::TimeDateProxy; use timedate_zbus::TimeDateProxy;
use zbus::Connection;
use icu::{ use icu::{
calendar::DateTime, calendar::DateTime,
@ -59,12 +57,10 @@ pub struct Window {
token_tx: Option<calloop::channel::Sender<TokenRequest>>, token_tx: Option<calloop::channel::Sender<TokenRequest>>,
config: TimeAppletConfig, config: TimeAppletConfig,
locale: Locale, locale: Locale,
conn: Option<Connection>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Message { pub enum Message {
Init(Option<Connection>),
TogglePopup, TogglePopup,
CloseRequested(window::Id), CloseRequested(window::Id),
Tick, Tick,
@ -75,7 +71,7 @@ pub enum Message {
OpenDateTimeSettings, OpenDateTimeSettings,
Token(TokenUpdate), Token(TokenUpdate),
ConfigChanged(TimeAppletConfig), ConfigChanged(TimeAppletConfig),
TimezoneUpdate(Option<String>), TimezoneUpdate(String),
} }
impl Window { impl Window {
@ -152,10 +148,8 @@ impl cosmic::Application for Window {
token_tx: None, token_tx: None,
config: TimeAppletConfig::default(), config: TimeAppletConfig::default(),
locale, locale,
conn: None,
}, },
Command::single(command::Action::Future(Box::pin(Connection::system()))) Command::none(),
.map(|res| Message::Init(res.ok()).into()),
) )
} }
@ -185,49 +179,43 @@ impl cosmic::Application for Window {
}) })
} }
let conn = self.conn.clone(); // Update applet's timezone if the system's timezone changes
fn timezone_subscription(conn: Option<Connection>) -> Subscription<Message> { async fn timezone_update(output: &mut mpsc::Sender<Message>) -> zbus::Result<()> {
use cosmic::iced_futures::futures::StreamExt; let conn = zbus::Connection::system().await?;
let Some(conn) = conn else { let proxy = TimeDateProxy::new(&conn).await?;
return Subscription::none();
};
// Update applet's timezone if the system's timezone changes // The stream always returns the current timezone as its first item even if it wasn't
subscription::unfold("timezone-sub", (), move |_| { // updated. If the proxy is recreated in a loop somehow, the resulting stream will
let conn = conn.clone(); // always yield an update immediately which could lead to spammed false updates.
async move { while let Some(property) = proxy.receive_timezone_changed().await.next().await {
TimeDateProxy::new(&conn) let tz = property.get().await?;
.inspect_err(|e| { output
tracing::error!("Failed to connect to timedate endpoint: {e:?}") .send(Message::TimezoneUpdate(tz))
}) .map_err(|e| {
.then(|proxy| async move { zbus::Error::InputOutput(std::sync::Arc::new(std::io::Error::other(e)))
let Ok(proxy) = proxy else { })
return (Message::TimezoneUpdate(None), ()); .await?;
}; }
proxy
.receive_timezone_changed() Ok(())
.then(|stream| stream.into_future()) }
.then(|(prop_opt, _)| async move {
if let Some(property) = prop_opt { fn timezone_subscription() -> Subscription<Message> {
property subscription::channel("timezone-sub", 1, |mut output| async move {
.get() 'retry: loop {
.await match timezone_update(&mut output).await {
.inspect_err(|e| { Ok(()) => break 'retry,
tracing::error!( Err(err) => {
"Failed to receive time zone update: {e:?}" tracing::error!(
) ?err,
}) "Automatic timezone updater failed; retrying in one minute"
.ok() );
.map(|tz| (Message::TimezoneUpdate(Some(tz)), ())) tokio::time::sleep(std::time::Duration::from_secs(60)).await;
.unwrap_or((Message::TimezoneUpdate(None), ())) }
} else { }
(Message::TimezoneUpdate(None), ())
}
})
.await
})
.await
} }
std::future::pending().await
}) })
} }
@ -235,7 +223,7 @@ impl cosmic::Application for Window {
rectangle_tracker_subscription(0).map(|e| Message::Rectangle(e.1)), rectangle_tracker_subscription(0).map(|e| Message::Rectangle(e.1)),
time_subscription().map(|_| Message::Tick), time_subscription().map(|_| Message::Tick),
activation_token_subscription(0).map(Message::Token), activation_token_subscription(0).map(Message::Token),
timezone_subscription(conn), timezone_subscription(),
self.core.watch_config(Self::APP_ID).map(|u| { self.core.watch_config(Self::APP_ID).map(|u| {
for err in u.errors { for err in u.errors {
tracing::error!(?err, "Error watching config"); tracing::error!(?err, "Error watching config");
@ -250,10 +238,6 @@ impl cosmic::Application for Window {
message: Self::Message, message: Self::Message,
) -> cosmic::iced::Command<app::Message<Self::Message>> { ) -> cosmic::iced::Command<app::Message<Self::Message>> {
match message { match message {
Message::Init(conn) => {
self.conn = conn;
Command::none()
}
Message::TogglePopup => { Message::TogglePopup => {
if let Some(p) = self.popup.take() { if let Some(p) = self.popup.take() {
destroy_popup(p) destroy_popup(p)
@ -376,9 +360,7 @@ impl cosmic::Application for Window {
Command::none() Command::none()
} }
Message::TimezoneUpdate(timezone) => { Message::TimezoneUpdate(timezone) => {
if let Some(timezone) = if let Ok(timezone) = timezone.parse::<chrono_tz::Tz>() {
timezone.and_then(|timezone| timezone.parse::<chrono_tz::Tz>().ok())
{
self.now = chrono::Local::now().with_timezone(&timezone).fixed_offset(); self.now = chrono::Local::now().with_timezone(&timezone).fixed_offset();
self.timezone = Some(timezone); self.timezone = Some(timezone);
} }