yazi_core/notify/commands/
tick.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
use std::time::Duration;

use ratatui::layout::Rect;
use yazi_macro::emit;
use yazi_shared::{Layer, event::{Cmd, CmdCow, Data}};

use crate::notify::Notify;

pub struct Opt {
	interval: Duration,
}

impl TryFrom<CmdCow> for Opt {
	type Error = ();

	fn try_from(c: CmdCow) -> Result<Self, Self::Error> {
		let interval = c.first().and_then(Data::as_f64).ok_or(())?;
		if interval < 0.0 {
			return Err(());
		}

		Ok(Self { interval: Duration::from_secs_f64(interval) })
	}
}

impl TryFrom<Cmd> for Opt {
	type Error = ();

	fn try_from(c: Cmd) -> Result<Self, Self::Error> { Self::try_from(CmdCow::from(c)) }
}

impl Notify {
	pub fn tick(&mut self, opt: impl TryInto<Opt>, area: Rect) {
		self.tick_handle.take().map(|h| h.abort());
		let Ok(opt) = opt.try_into() else {
			return;
		};

		let limit = self.limit(area);
		if limit == 0 {
			return;
		}

		for m in &mut self.messages[..limit] {
			if m.timeout.is_zero() {
				m.percent = m.percent.saturating_sub(20);
			} else if m.percent < 100 {
				m.percent += 20;
			} else {
				m.timeout = m.timeout.saturating_sub(opt.interval);
			}
		}

		self.messages.retain(|m| m.percent > 0 || !m.timeout.is_zero());
		let limit = self.limit(area);
		let timeouts: Vec<_> = self.messages[..limit]
			.iter()
			.filter(|&m| m.percent == 100 && !m.timeout.is_zero())
			.map(|m| m.timeout)
			.collect();

		let interval = if timeouts.len() != limit {
			Duration::from_millis(50)
		} else if let Some(min) = timeouts.iter().min() {
			*min
		} else {
			return;
		};

		self.tick_handle = Some(tokio::spawn(async move {
			tokio::time::sleep(interval).await;
			emit!(Call(Cmd::args("update_notify", &[interval.as_secs_f64()]), Layer::App));
		}));
	}
}