fix(time): Update applet timezone on change
Closes: #582 The chrono crate caches the local timezone but doesn't update it. This makes sense because it'd be inefficient to constantly evaluate the local timezone. Instead of using the local timezone, this patch changes the applet to use a fixed offset internally which is updated if the external timezone changes.
This commit is contained in:
parent
f43705d31f
commit
323e8a55b2
3 changed files with 139 additions and 5 deletions
52
Cargo.lock
generated
52
Cargo.lock
generated
|
|
@ -706,6 +706,28 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz-build",
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz-build"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1"
|
||||
dependencies = [
|
||||
"parse-zoneinfo",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.4.0"
|
||||
|
|
@ -1141,16 +1163,19 @@ name = "cosmic-applet-time"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"i18n-embed 0.14.1",
|
||||
"i18n-embed-fl 0.8.0",
|
||||
"icu",
|
||||
"libcosmic",
|
||||
"once_cell",
|
||||
"rust-embed 8.5.0",
|
||||
"timedate-zbus",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-log",
|
||||
"tracing-subscriber",
|
||||
"zbus 4.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4436,6 +4461,15 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse-zoneinfo"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
|
|
@ -4458,6 +4492,16 @@ dependencies = [
|
|||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.2"
|
||||
|
|
@ -5705,6 +5749,14 @@ dependencies = [
|
|||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "timedate-zbus"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/pop-os/dbus-settings-bindings#cd21ddcb1b5cbfc80ab84b34d3c8b1ff3d81179a"
|
||||
dependencies = [
|
||||
"zbus 4.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ license = "GPL-3.0"
|
|||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.35", features = ["clock"] }
|
||||
chrono-tz = "0.9"
|
||||
i18n-embed-fl.workspace = true
|
||||
i18n-embed.workspace = true
|
||||
libcosmic.workspace = true
|
||||
|
|
@ -15,4 +16,6 @@ tokio = { version = "1.36.0", features = ["time"] }
|
|||
tracing-log.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing.workspace = true
|
||||
icu = { version = "1.4.0", features = ["experimental", "compiled_data", "icu_datetime_experimental"]}
|
||||
icu = { version = "1.4.0", features = ["experimental", "compiled_data", "icu_datetime_experimental"]}
|
||||
zbus.workspace = true
|
||||
timedate-zbus = { git = "https://github.com/pop-os/dbus-settings-bindings" }
|
||||
|
|
|
|||
|
|
@ -9,12 +9,14 @@ use cosmic::{
|
|||
applet::{cosmic_panel_config::PanelAnchor, menu_button, padded_control},
|
||||
cctk::sctk::reexports::calloop,
|
||||
iced::{
|
||||
futures::{FutureExt, TryFutureExt},
|
||||
subscription,
|
||||
wayland::popup::{destroy_popup, get_popup},
|
||||
widget::{column, row, text, vertical_space},
|
||||
window, Alignment, Length, Rectangle, Subscription,
|
||||
},
|
||||
iced_core::alignment::{Horizontal, Vertical},
|
||||
iced_runtime::command,
|
||||
iced_style::application,
|
||||
iced_widget::{horizontal_rule, Column},
|
||||
widget::{
|
||||
|
|
@ -23,6 +25,8 @@ use cosmic::{
|
|||
},
|
||||
Command, Element, Theme,
|
||||
};
|
||||
use timedate_zbus::TimeDateProxy;
|
||||
use zbus::Connection;
|
||||
|
||||
use icu::{
|
||||
calendar::DateTime,
|
||||
|
|
@ -47,17 +51,20 @@ use cosmic::applet::token::subscription::{
|
|||
pub struct Window {
|
||||
core: cosmic::app::Core,
|
||||
popup: Option<window::Id>,
|
||||
now: chrono::DateTime<chrono::Local>,
|
||||
now: chrono::DateTime<chrono::FixedOffset>,
|
||||
timezone: Option<chrono_tz::Tz>,
|
||||
date_selected: chrono::NaiveDate,
|
||||
rectangle_tracker: Option<RectangleTracker<u32>>,
|
||||
rectangle: Rectangle,
|
||||
token_tx: Option<calloop::channel::Sender<TokenRequest>>,
|
||||
config: TimeAppletConfig,
|
||||
locale: Locale,
|
||||
conn: Option<Connection>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
Init(Option<Connection>),
|
||||
TogglePopup,
|
||||
CloseRequested(window::Id),
|
||||
Tick,
|
||||
|
|
@ -68,6 +75,7 @@ pub enum Message {
|
|||
OpenDateTimeSettings,
|
||||
Token(TokenUpdate),
|
||||
ConfigChanged(TimeAppletConfig),
|
||||
TimezoneUpdate(Option<String>),
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
|
@ -126,21 +134,28 @@ impl cosmic::Application for Window {
|
|||
}
|
||||
};
|
||||
|
||||
let now: chrono::prelude::DateTime<chrono::prelude::Local> = chrono::Local::now();
|
||||
// Chrono evaluates the local timezone once whereby it's stored in a thread local
|
||||
// variable but never updated
|
||||
// Instead of using the local timezone, we will store an offset that is updated if the
|
||||
// timezone is ever externally changed
|
||||
let now: chrono::DateTime<chrono::FixedOffset> = chrono::Local::now().fixed_offset();
|
||||
|
||||
(
|
||||
Self {
|
||||
core,
|
||||
popup: None,
|
||||
now,
|
||||
timezone: None,
|
||||
date_selected: chrono::NaiveDate::from(now.naive_local()),
|
||||
rectangle_tracker: None,
|
||||
rectangle: Rectangle::default(),
|
||||
token_tx: None,
|
||||
config: TimeAppletConfig::default(),
|
||||
locale,
|
||||
conn: None,
|
||||
},
|
||||
Command::none(),
|
||||
Command::single(command::Action::Future(Box::pin(Connection::system())))
|
||||
.map(|res| Message::Init(res.ok()).into()),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -170,10 +185,57 @@ impl cosmic::Application for Window {
|
|||
})
|
||||
}
|
||||
|
||||
let conn = self.conn.clone();
|
||||
fn timezone_subscription(conn: Option<Connection>) -> Subscription<Message> {
|
||||
use cosmic::iced_futures::futures::StreamExt;
|
||||
let Some(conn) = conn else {
|
||||
return Subscription::none();
|
||||
};
|
||||
|
||||
// Update applet's timezone if the system's timezone changes
|
||||
subscription::unfold("timezone-sub", (), move |_| {
|
||||
let conn = conn.clone();
|
||||
async move {
|
||||
TimeDateProxy::new(&conn)
|
||||
.inspect_err(|e| {
|
||||
tracing::error!("Failed to connect to timedate endpoint: {e:?}")
|
||||
})
|
||||
.then(|proxy| async move {
|
||||
let Ok(proxy) = proxy else {
|
||||
return (Message::TimezoneUpdate(None), ());
|
||||
};
|
||||
proxy
|
||||
.receive_timezone_changed()
|
||||
.then(|stream| stream.into_future())
|
||||
.then(|(prop_opt, _)| async move {
|
||||
if let Some(property) = prop_opt {
|
||||
property
|
||||
.get()
|
||||
.await
|
||||
.inspect_err(|e| {
|
||||
tracing::error!(
|
||||
"Failed to receive time zone update: {e:?}"
|
||||
)
|
||||
})
|
||||
.ok()
|
||||
.map(|tz| (Message::TimezoneUpdate(Some(tz)), ()))
|
||||
.unwrap_or((Message::TimezoneUpdate(None), ()))
|
||||
} else {
|
||||
(Message::TimezoneUpdate(None), ())
|
||||
}
|
||||
})
|
||||
.await
|
||||
})
|
||||
.await
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Subscription::batch(vec![
|
||||
rectangle_tracker_subscription(0).map(|e| Message::Rectangle(e.1)),
|
||||
time_subscription().map(|_| Message::Tick),
|
||||
activation_token_subscription(0).map(Message::Token),
|
||||
timezone_subscription(conn),
|
||||
self.core.watch_config(Self::APP_ID).map(|u| {
|
||||
for err in u.errors {
|
||||
tracing::error!(?err, "Error watching config");
|
||||
|
|
@ -188,6 +250,10 @@ impl cosmic::Application for Window {
|
|||
message: Self::Message,
|
||||
) -> cosmic::iced::Command<app::Message<Self::Message>> {
|
||||
match message {
|
||||
Message::Init(conn) => {
|
||||
self.conn = conn;
|
||||
Command::none()
|
||||
}
|
||||
Message::TogglePopup => {
|
||||
if let Some(p) = self.popup.take() {
|
||||
destroy_popup(p)
|
||||
|
|
@ -220,7 +286,10 @@ impl cosmic::Application for Window {
|
|||
}
|
||||
}
|
||||
Message::Tick => {
|
||||
self.now = chrono::Local::now();
|
||||
self.now = self
|
||||
.timezone
|
||||
.map(|tz| chrono::Local::now().with_timezone(&tz).fixed_offset())
|
||||
.unwrap_or_else(|| chrono::Local::now().into());
|
||||
Command::none()
|
||||
}
|
||||
Message::Rectangle(u) => {
|
||||
|
|
@ -306,6 +375,16 @@ impl cosmic::Application for Window {
|
|||
self.config = c;
|
||||
Command::none()
|
||||
}
|
||||
Message::TimezoneUpdate(timezone) => {
|
||||
if let Some(timezone) =
|
||||
timezone.and_then(|timezone| timezone.parse::<chrono_tz::Tz>().ok())
|
||||
{
|
||||
self.now = chrono::Local::now().with_timezone(&timezone).fixed_offset();
|
||||
self.timezone = Some(timezone);
|
||||
}
|
||||
|
||||
self.update(Message::Tick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue