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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! Print the program's interface and keep track of the selection state
//!
//! The selection state is done here because this task receives both the person's interactions and
//! the results from the search engine. That is, it "knows" what the person sees and why they are
//! moving, typing, etc.
//!
//! When the program finishes this is the task that will return the final person's selection.

use crate::common::{Result, Text};
use crate::config::Config;
use crate::events::Event;
use crate::state::State;
use crate::ui::Canvas;
use async_std::channel::Receiver;
use async_std::io;
use async_std::prelude::*;
use std::time::Instant;

/// Run the screen's task
pub async fn task<W>(config: Config, outbound: W, mut recv: Receiver<Event>) -> Result<Option<Text>>
where
    W: io::Write + Send + Unpin + 'static,
{
    log::trace!("starting screen");

    let mut last_timestamp = Instant::now();
    let mut render: bool;
    let mut selection = None;

    let mut state = State::new();
    let mut canvas = Canvas::new(&config, outbound).await?;

    canvas.render(&state).await?;

    while let Some(event) = recv.next().await {
        render = false;

        match event {
            Event::Search(prompt) => {
                log::trace!("printing prompt: {:?}", prompt);

                last_timestamp = prompt.timestamp();
                state.set_search(prompt);
                render = true;
            }

            Event::Flush((matches, len)) => {
                log::trace!("flushing matches");

                // Flush happens when the pool size
                // changes or the pool is complete
                state.set_matches((matches, len));
                render = true;
            }

            Event::SearchDone((matches, len, timestamp)) => {
                // Only if the search timestamp is the same as the last query timestamp
                // we will update the state. This way we will drop any intermediate search
                // and reduce the number of renders
                if timestamp >= last_timestamp {
                    log::trace!("printing new search results");

                    state.set_matches((matches, len));
                    render = true;
                }
            }

            Event::Up => {
                log::trace!("moving selection up");

                state.select_up();
                render = true;
            }
            Event::Down => {
                log::trace!("moving selection down");

                state.select_down();
                render = true;
            }

            Event::Done => {
                selection = state.selection();
                break;
            }
            Event::Exit => break,

            _ => (),
        };

        if render {
            canvas.render(&state).await?;
        }
    }

    log::trace!("screen done");

    Ok(selection)
}