ai: add tooltip for location/update time
This commit is contained in:
parent
1c4fc30f4b
commit
e45b1fc1b6
1 changed files with 66 additions and 9 deletions
75
src/app.rs
75
src/app.rs
|
|
@ -4,7 +4,7 @@ use cosmic::iced::{Color, Font, Limits, Rectangle, Subscription, window};
|
|||
use cosmic::iced::alignment::{Horizontal, Vertical};
|
||||
use cosmic::iced::platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup};
|
||||
use cosmic::widget::{self, autosize, container, rectangle_tracker::*};
|
||||
use cosmic::{iced_futures, prelude::*};
|
||||
use cosmic::{iced_futures, prelude::*, surface};
|
||||
use cosmic::applet::cosmic_panel_config::PanelAnchor;
|
||||
use cosmic::iced_core::Shadow;
|
||||
use futures_util::SinkExt;
|
||||
|
|
@ -17,8 +17,12 @@ const WEATHER_UPDATE_INTERVAL_MINUTES: u64 = 15;
|
|||
pub struct AppModel {
|
||||
/// Application state which is managed by the COSMIC runtime.
|
||||
core: cosmic::Core,
|
||||
/// Current weather data
|
||||
/// Current weather data (icon|temp)
|
||||
weather_text: String,
|
||||
/// Detected location from wttr.in
|
||||
location: String,
|
||||
/// Last successful update time
|
||||
last_updated: String,
|
||||
/// Full weather report for popup
|
||||
full_weather: String,
|
||||
/// Loading state
|
||||
|
|
@ -34,12 +38,13 @@ pub struct AppModel {
|
|||
/// Messages emitted by the applet.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
WeatherUpdate(Result<String, String>),
|
||||
WeatherUpdate(Result<(String, String), String>),
|
||||
FullWeatherUpdate(Result<String, String>),
|
||||
RefreshWeather,
|
||||
TogglePopup,
|
||||
CloseRequested(window::Id),
|
||||
Rectangle(RectangleUpdate<u32>),
|
||||
Surface(surface::Action),
|
||||
}
|
||||
|
||||
impl cosmic::Application for AppModel {
|
||||
|
|
@ -63,6 +68,8 @@ impl cosmic::Application for AppModel {
|
|||
let app = AppModel {
|
||||
core,
|
||||
weather_text: "Loading...".to_string(),
|
||||
location: String::new(),
|
||||
last_updated: String::new(),
|
||||
full_weather: String::new(),
|
||||
is_loading: true,
|
||||
popup: None,
|
||||
|
|
@ -100,12 +107,29 @@ impl cosmic::Application for AppModel {
|
|||
}
|
||||
};
|
||||
|
||||
let has_popup = self.popup.is_some();
|
||||
let tooltip_text = if self.location.is_empty() {
|
||||
"Loading...".to_string()
|
||||
} else if self.last_updated.is_empty() {
|
||||
self.location.clone()
|
||||
} else {
|
||||
format!("{}\nUpdated: {}", self.location, self.last_updated)
|
||||
};
|
||||
|
||||
let tooltip = self.core.applet.applet_tooltip(
|
||||
button,
|
||||
tooltip_text,
|
||||
has_popup,
|
||||
Message::Surface,
|
||||
None,
|
||||
);
|
||||
|
||||
let limits = Limits::NONE.min_width(1.).min_height(1.);
|
||||
|
||||
let element: Element<'_, Self::Message> = if let Some(tracker) = self.rectangle_tracker.as_ref() {
|
||||
tracker.container(0, button).ignore_bounds(true).into()
|
||||
tracker.container(0, tooltip).ignore_bounds(true).into()
|
||||
} else {
|
||||
container(button).into()
|
||||
container(tooltip).into()
|
||||
};
|
||||
|
||||
autosize::autosize(
|
||||
|
|
@ -206,7 +230,11 @@ impl cosmic::Application for AppModel {
|
|||
Message::WeatherUpdate(result) => {
|
||||
self.is_loading = false;
|
||||
match result {
|
||||
Ok(weather) => self.weather_text = weather,
|
||||
Ok((location, weather)) => {
|
||||
self.location = location;
|
||||
self.weather_text = weather;
|
||||
self.last_updated = format_current_time();
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Weather fetch error: {}", e);
|
||||
self.weather_text = if e.contains("network") || e.contains("timeout") {
|
||||
|
|
@ -307,17 +335,46 @@ impl cosmic::Application for AppModel {
|
|||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::Surface(action) => {
|
||||
cosmic::task::message(cosmic::Action::Cosmic(
|
||||
cosmic::app::Action::Surface(action),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_weather() -> Result<String, String> {
|
||||
let response = reqwest::get(&format!("{}/?format=%c|%t", WTTR_URL))
|
||||
async fn fetch_weather() -> Result<(String, String), String> {
|
||||
let response = reqwest::get(&format!("{}/?format=%l|%c|%t", WTTR_URL))
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let text = response.text().await.map_err(|e| e.to_string())?;
|
||||
Ok(text.trim().to_string())
|
||||
let text = text.trim();
|
||||
// Format: "location|icon|temp"
|
||||
let parts: Vec<&str> = text.splitn(3, '|').collect();
|
||||
if parts.len() == 3 {
|
||||
let location = parts[0].to_string();
|
||||
let weather = format!("{}|{}", parts[1], parts[2]);
|
||||
Ok((location, weather))
|
||||
} else {
|
||||
Ok((String::new(), text.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn format_current_time() -> String {
|
||||
// Include %H:%M in the wttr.in request would add another field to parse;
|
||||
// instead, record the local time when we receive the update.
|
||||
// We use /proc/self or a simple UTC-based approach. Since the applet
|
||||
// runs locally, we can shell out or use libc. For simplicity, use
|
||||
// the `date` command which respects the user's timezone.
|
||||
std::process::Command::new("date")
|
||||
.arg("+%H:%M")
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|o| String::from_utf8(o.stdout).ok())
|
||||
.map(|s| s.trim().to_string())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
async fn fetch_full_weather() -> Result<String, String> {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue