Skip to content

Commit 13a9a5d

Browse files
committed
add a notification in the tray icon that an update is available
1 parent d07ff2c commit 13a9a5d

7 files changed

Lines changed: 154 additions & 4 deletions

File tree

Cargo.lock

Lines changed: 71 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ icns = "0.3.1"
1818
image = "0.25.9"
1919
libc = "0.2.180"
2020
log = "0.4.29"
21+
minreq = { version = "2.14.1", features = ["https"] }
2122
objc2 = "0.6.3"
2223
objc2-app-kit = { version = "0.3.2", features = ["NSImage"] }
2324
objc2-application-services = { version = "0.3.2", default-features = false, features = [
@@ -30,6 +31,7 @@ once_cell = "1.21.3"
3031
rand = "0.9.2"
3132
rayon = "1.11.0"
3233
serde = { version = "1.0.228", features = ["derive"] }
34+
serde_json = "1.0.149"
3335
tokio = { version = "1.48.0", features = ["full"] }
3436
toml = "0.9.8"
3537
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }

src/app.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ pub enum Move {
4949
/// The message type that iced uses for actions that can do something
5050
#[derive(Debug, Clone)]
5151
pub enum Message {
52+
UpdateAvailable,
5253
ResizeWindow(Id, f32),
5354
OpenWindow,
5455
SearchQueryChanged(String, Id),

src/app/menubar.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use tokio::runtime::Runtime;
2626
/// This create a new menubar icon for the app
2727
pub fn menu_icon(config: Config, sender: ExtSender) -> TrayIcon {
2828
let builder = TrayIconBuilder::new();
29-
let menu = menu_builder(config, sender);
29+
let menu = menu_builder(config, sender, false);
3030

3131
let image = get_image();
3232
let icon = Icon::from_rgba(image.as_bytes().to_vec(), image.width(), image.height()).unwrap();
@@ -38,7 +38,7 @@ pub fn menu_icon(config: Config, sender: ExtSender) -> TrayIcon {
3838
.unwrap()
3939
}
4040

41-
pub fn menu_builder(config: Config, sender: ExtSender) -> Menu {
41+
pub fn menu_builder(config: Config, sender: ExtSender, update_item: bool) -> Menu {
4242
let hotkey = config.toggle_hotkey.parse::<HotKey>().unwrap();
4343

4444
let mut modes = config.modes;
@@ -49,6 +49,16 @@ pub fn menu_builder(config: Config, sender: ExtSender) -> Menu {
4949
init_event_handler(sender, hotkey.id());
5050

5151
Menu::with_items(&[
52+
&MenuItem::with_id(
53+
"update",
54+
if update_item {
55+
"Update available"
56+
} else {
57+
"Up to date"
58+
},
59+
update_item,
60+
None,
61+
),
5262
&version_item(),
5363
&about_item(get_image()),
5464
&open_github_item(),
@@ -104,6 +114,9 @@ fn init_event_handler(sender: ExtSender, hotkey_id: u32) {
104114
.unwrap();
105115
});
106116
}
117+
"update" => {
118+
open_url("https://github.com/unsecretised/rustcast/releases/latest");
119+
}
107120
"open_discord" => {
108121
open_url(DISCORD_LINK);
109122
}

src/app/tile.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use iced::{
2222
};
2323
use iced::{event, window};
2424

25-
use log::info;
25+
use log::{info, warn};
2626
use objc2::rc::Retained;
2727
use objc2_app_kit::NSRunningApplication;
2828
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
@@ -31,6 +31,7 @@ use tray_icon::TrayIcon;
3131
use std::fmt::Debug;
3232
use std::fs;
3333
use std::ops::Bound;
34+
use std::str::FromStr;
3435
use std::time::Duration;
3536
use std::{collections::BTreeMap, path::Path};
3637

@@ -109,6 +110,7 @@ pub struct Tile {
109110
pub focus_id: u32,
110111
pub query: String,
111112
pub current_mode: String,
113+
pub update_available: bool,
112114
query_lc: String,
113115
results: Vec<App>,
114116
options: AppIndex,
@@ -171,6 +173,7 @@ impl Tile {
171173
Subscription::run(handle_hotkeys),
172174
keyboard,
173175
Subscription::run(handle_recipient),
176+
Subscription::run(check_version),
174177
Subscription::run(handle_hot_reloading),
175178
Subscription::run(handle_clipboard_history),
176179
window::close_events().map(Message::HideWindow),
@@ -393,3 +396,51 @@ fn handle_recipient() -> impl futures::Stream<Item = Message> {
393396
}
394397
})
395398
}
399+
400+
fn check_version() -> impl futures::Stream<Item = Message> {
401+
stream::channel(100, async |mut output| {
402+
let current_version = format!("\"{}\"", option_env!("APP_VERSION").unwrap_or(""));
403+
404+
if current_version.is_empty() {
405+
println!("empty version");
406+
return;
407+
}
408+
409+
let req = minreq::Request::new(
410+
minreq::Method::Get,
411+
"https://api.github.com/repos/unsecretised/rustcast/releases/latest",
412+
)
413+
.with_header("User-Agent", "rustcast-update-checker")
414+
.with_header("Accept", "application/vnd.github+json")
415+
.with_header("X-GitHub-Api-Version", "2022-11-28");
416+
417+
loop {
418+
let resp = req
419+
.clone()
420+
.send()
421+
.and_then(|x| x.as_str().map(serde_json::Value::from_str));
422+
423+
info!("Made a req for latest version");
424+
425+
if let Ok(Ok(val)) = resp {
426+
let new_ver = val
427+
.get("name")
428+
.map(|x| x.to_string())
429+
.unwrap_or("".to_string());
430+
431+
// new_ver is in the format "\"v0.0.0\""
432+
// note that it is encapsulated in double quotes
433+
if new_ver.trim() != current_version
434+
&& !new_ver.is_empty()
435+
&& new_ver.starts_with("\"v")
436+
{
437+
info!("new version available: {new_ver}");
438+
output.send(Message::UpdateAvailable).await.ok();
439+
}
440+
} else {
441+
warn!("Error getting resp");
442+
}
443+
tokio::time::sleep(Duration::from_secs(60)).await;
444+
}
445+
})
446+
}

src/app/tile/elm.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ pub fn new(hotkey: HotKey, config: &Config) -> (Tile, Task<Message>) {
6262

6363
(
6464
Tile {
65+
update_available: false,
6566
current_mode: "Default".to_string(),
6667
query: String::new(),
6768
query_lc: String::new(),

src/app/tile/update.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
4545
Task::none()
4646
}
4747

48+
Message::UpdateAvailable => {
49+
tile.update_available = true;
50+
Task::done(Message::ReloadConfig)
51+
}
52+
4853
Message::SwitchMode(mode) => {
4954
if let Some(command) = tile.config.modes.get(mode.trim()) {
5055
tile.current_mode = mode.clone();
@@ -245,9 +250,15 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
245250
icon.set_menu(Some(Box::new(menu_builder(
246251
new_config.clone(),
247252
tile.sender.clone().unwrap(),
253+
tile.update_available,
248254
))));
249255
} else {
250256
tile.tray_icon = Some(menu_icon(new_config.clone(), tile.sender.clone().unwrap()));
257+
tile.tray_icon
258+
.as_mut()
259+
.unwrap()
260+
.set_visible(tile.config.show_trayicon)
261+
.ok();
251262
}
252263

253264
let mut new_options = get_installed_apps(new_config.theme.show_icons);
@@ -542,7 +553,7 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
542553
.map(|conversion| conversion.to_app())
543554
.collect();
544555
return single_item_resize_task(id);
545-
} else if let Some(res) = Expr::from_str(&tile.query).ok() {
556+
} else if let Ok(res) = Expr::from_str(&tile.query) {
546557
tile.results.push(App {
547558
ranking: 0,
548559
open_command: AppCommand::Function(Function::Calculate(res.clone())),

0 commit comments

Comments
 (0)