summaryrefslogtreecommitdiffhomepage
path: root/mullvad-cli/src/interactive/interface/error_handler.rs
blob: 710fb8c3867afef4e5b9871449f41edbb9640b2c (plain)
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
use super::{app::AppActions, dialog::Dialog};
use crate::interactive::component::{Component, Frame};

use crossterm::event::{Event, KeyCode, KeyEvent};
use parking_lot::Mutex;
use std::sync::Arc;
use tui::layout::Rect;

pub struct ErrorMessage {
    pub message: String,
    pub ignorable: bool,
}

pub struct ErrorHandler<T: Component> {
    actions: AppActions,
    error: Arc<Mutex<Option<ErrorMessage>>>,
    child: T,
}

impl<T: Component> ErrorHandler<T> {
    pub fn new(actions: AppActions, receiver: flume::Receiver<ErrorMessage>, child: T) -> Self {
        let new_error_handler = Self {
            actions,
            error: Arc::new(Mutex::new(None)),
            child,
        };

        new_error_handler.listen_error_messages(receiver);
        new_error_handler
    }

    fn listen_error_messages(&self, receiver: flume::Receiver<ErrorMessage>) {
        let actions = self.actions.clone();
        let error = self.error.clone();
        tokio::spawn(async move {
            while let Ok(new_error) = receiver.recv_async().await {
                {
                    let mut error = error.lock();
                    *error = Some(new_error);
                }
                actions.redraw_async().await;
            }
        });
    }
}

impl<T: Component> Component for ErrorHandler<T> {
    fn draw(&mut self, f: &mut Frame<'_>, area: Rect) {
        self.child.draw(f, area);

        if let Some(ref error) = *self.error.lock() {
            Dialog::new("An error occured", &error.message).draw(f, area);
        }
    }

    fn handle_event(&mut self, event: Event) {
        let mut error = self.error.lock();
        if let Some(ErrorMessage { ignorable, .. }) = *error {
            if ignorable && event == Event::Key(KeyEvent::from(KeyCode::Esc)) {
                *error = None;
                self.actions.redraw();
            }
        } else {
            self.child.handle_event(event);
        }
    }
}