mecomp_tui/state/
popup.rs

1//! This module implements the popup state store.
2//! Which handles opening and closing popups.
3
4use tokio::sync::{
5    broadcast,
6    mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
7};
8
9use crate::{state::action::PopupAction, termination::Interrupted, ui::widgets::popups::PopupType};
10
11/// The popup state store.
12#[derive(Debug, Clone)]
13#[allow(clippy::module_name_repetitions)]
14pub struct PopupState {
15    state_tx: UnboundedSender<Option<PopupType>>,
16}
17
18#[allow(clippy::module_name_repetitions)]
19pub type PopupStateReceiver = UnboundedReceiver<Option<PopupType>>;
20
21impl PopupState {
22    /// create a new popup state store, and return the receiver for listening to state updates.
23    #[must_use]
24    pub fn new() -> (Self, PopupStateReceiver) {
25        let (state_tx, state_rx) = unbounded_channel::<Option<PopupType>>();
26
27        (Self { state_tx }, state_rx)
28    }
29
30    /// a loop that updates the popup state every tick.
31    ///
32    /// # Errors
33    ///
34    /// Fails if the state cannot be sent
35    pub async fn main_loop(
36        &self,
37        mut action_rx: UnboundedReceiver<PopupAction>,
38        mut interrupt_rx: broadcast::Receiver<Interrupted>,
39    ) -> anyhow::Result<Interrupted> {
40        // the initial state once
41        self.state_tx.send(None)?;
42
43        // popup stack
44        let mut stack = Vec::new();
45
46        let result = loop {
47            tokio::select! {
48                // Handle the actions coming from the UI
49                // and process them to do async operations
50                Some(action) = action_rx.recv() => {
51                    match action {
52                        PopupAction::Open(popup) => {
53                            stack.push(popup.clone());
54                        }
55                        PopupAction::Close => {
56                            stack.pop();
57                        }
58                    }
59                    self.state_tx.send(stack.last().cloned())?;
60                }
61                // Catch and handle interrupt signal to gracefully shutdown
62                Ok(interrupted) = interrupt_rx.recv() => {
63                    break interrupted;
64                }
65            }
66        };
67
68        Ok(result)
69    }
70}