|
1 |
| -use cosmic::iced::Subscription; |
2 |
| -use futures::stream; |
3 |
| -use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher}; |
4 |
| -use std::{fmt::Debug, hash::Hash}; |
5 |
| -use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver}; |
6 |
| - |
7 |
| -#[derive(Debug)] |
8 |
| -pub enum State { |
9 |
| - Ready, |
10 |
| - Waiting { |
11 |
| - watcher: RecommendedWatcher, |
12 |
| - rx: UnboundedReceiver<notify::Result<Event>>, |
13 |
| - }, |
14 |
| - Finished, |
15 |
| -} |
| 1 | +use cosmic::{ |
| 2 | + iced::{stream, Subscription}, |
| 3 | + iced_futures::futures::{self, SinkExt}, |
| 4 | +}; |
| 5 | +use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; |
| 6 | +use std::fmt::Debug; |
| 7 | +use std::hash::Hash; |
| 8 | +use tokio::sync::mpsc; |
16 | 9 |
|
17 | 10 | #[derive(Debug, Clone, Copy)]
|
18 |
| -pub enum DesktopFileEvent { |
| 11 | +pub enum Event { |
19 | 12 | Changed,
|
20 | 13 | }
|
21 | 14 |
|
22 | 15 | pub fn desktop_files<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
23 | 16 | id: I,
|
24 |
| -) -> cosmic::iced::Subscription<(I, DesktopFileEvent)> { |
| 17 | +) -> cosmic::iced::Subscription<Event> { |
25 | 18 | Subscription::run_with_id(
|
26 | 19 | id,
|
27 |
| - stream::unfold(State::Ready, move |mut state| async move { |
28 |
| - let (event, new_state) = start_watching(id, state).await; |
29 |
| - state = new_state; |
30 |
| - if let Some(event) = event { |
31 |
| - return Some((event, state)); |
32 |
| - } else { |
33 |
| - None |
34 |
| - } |
35 |
| - }), |
36 |
| - ) |
37 |
| -} |
38 |
| - |
39 |
| -async fn start_watching<I: Copy>(id: I, state: State) -> (Option<(I, DesktopFileEvent)>, State) { |
40 |
| - match state { |
41 |
| - State::Ready => { |
42 |
| - let paths = freedesktop_desktop_entry::default_paths(); |
43 |
| - // TODO log errors |
44 |
| - if let Ok((mut watcher, rx)) = async_watcher() { |
45 |
| - for path in paths { |
| 20 | + stream::channel(50, move |mut output| async move { |
| 21 | + let handle = tokio::runtime::Handle::current(); |
| 22 | + let (tx, mut rx) = mpsc::channel(4); |
| 23 | + let mut last_update = std::time::Instant::now(); |
| 24 | + |
| 25 | + // Automatically select the best implementation for your platform. |
| 26 | + // You can also access each implementation directly e.g. INotifyWatcher. |
| 27 | + let watcher = RecommendedWatcher::new( |
| 28 | + move |res: Result<notify::Event, notify::Error>| { |
| 29 | + if let Ok(event) = res { |
| 30 | + match event.kind { |
| 31 | + EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) => { |
| 32 | + let now = std::time::Instant::now(); |
| 33 | + if now.duration_since(last_update).as_secs() > 3 { |
| 34 | + _ = handle.block_on(tx.send(())); |
| 35 | + last_update = now; |
| 36 | + } |
| 37 | + } |
| 38 | + |
| 39 | + _ => (), |
| 40 | + } |
| 41 | + } |
| 42 | + }, |
| 43 | + Config::default(), |
| 44 | + ); |
| 45 | + |
| 46 | + if let Ok(mut watcher) = watcher { |
| 47 | + for path in freedesktop_desktop_entry::default_paths() { |
46 | 48 | let _ = watcher.watch(path.as_ref(), RecursiveMode::Recursive);
|
47 | 49 | }
|
48 |
| - ( |
49 |
| - Some((id, DesktopFileEvent::Changed)), |
50 |
| - State::Waiting { watcher, rx }, |
51 |
| - ) |
52 |
| - } else { |
53 |
| - (None, State::Finished) |
54 |
| - } |
55 |
| - } |
56 |
| - State::Waiting { watcher, rx } => { |
57 |
| - if let Some(rx) = async_watch(rx).await { |
58 |
| - ( |
59 |
| - Some((id, DesktopFileEvent::Changed)), |
60 |
| - State::Waiting { watcher, rx }, |
61 |
| - ) |
62 |
| - } else { |
63 |
| - (None, State::Finished) |
64 |
| - } |
65 |
| - } |
66 |
| - State::Finished => cosmic::iced::futures::future::pending().await, |
67 |
| - } |
68 |
| -} |
69 |
| - |
70 |
| -fn async_watcher() -> notify::Result<(RecommendedWatcher, UnboundedReceiver<notify::Result<Event>>)> |
71 |
| -{ |
72 |
| - let (tx, rx) = unbounded_channel(); |
73 |
| - |
74 |
| - // Automatically select the best implementation for your platform. |
75 |
| - // You can also access each implementation directly e.g. INotifyWatcher. |
76 |
| - let watcher = RecommendedWatcher::new( |
77 |
| - move |res| { |
78 |
| - futures::executor::block_on(async { |
79 |
| - let _ = tx.send(res); |
80 |
| - }) |
81 |
| - }, |
82 |
| - Config::default(), |
83 |
| - )?; |
84 |
| - |
85 |
| - Ok((watcher, rx)) |
86 |
| -} |
87 | 50 |
|
88 |
| -async fn async_watch( |
89 |
| - mut rx: UnboundedReceiver<notify::Result<Event>>, |
90 |
| -) -> Option<UnboundedReceiver<notify::Result<Event>>> { |
91 |
| - // TODO log errors |
92 |
| - if let Some(res) = rx.recv().await { |
93 |
| - match res { |
94 |
| - Ok(_) => return Some(rx), |
95 |
| - Err(_) => return None, |
96 |
| - } |
97 |
| - } |
| 51 | + while rx.recv().await.is_some() { |
| 52 | + _ = output.send(Event::Changed).await; |
| 53 | + } |
| 54 | + } |
98 | 55 |
|
99 |
| - None |
| 56 | + futures::future::pending().await |
| 57 | + }), |
| 58 | + ) |
100 | 59 | }
|
0 commit comments