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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! Search screen key handler.
use anyhow::Result;
use crossterm::event::{KeyCode, KeyEvent};
use crate::endpoints::roms::GetRoms;
use crate::types::RomList;
use super::super::background::types::{SearchLoadDone, SearchLoadEvent};
use super::super::{App, AppScreen};
use crate::tui::screens::{GameDetailPrevious, GameDetailScreen, MainMenuScreen};
impl App {
pub(in crate::tui::app) async fn handle_search(&mut self, key: &KeyEvent) -> Result<bool> {
let search = match &mut self.screen {
AppScreen::Search(s) => s,
_ => return Ok(false),
};
match key.code {
KeyCode::Backspace => search.delete_char(),
KeyCode::Left => search.cursor_left(),
KeyCode::Right => search.cursor_right(),
KeyCode::Up => search.previous(),
KeyCode::Down => search.next(),
KeyCode::Char(c) => search.add_char(c),
KeyCode::Enter => {
if search.query.is_empty() {
// no-op (same as before: empty query does not search)
} else if search.result_groups.is_some() && search.results_match_current_query() {
if let Some((primary, others)) = search.get_selected_group() {
let prev = std::mem::replace(
&mut self.screen,
AppScreen::MainMenu(MainMenuScreen::new()),
);
if let AppScreen::Search(s) = prev {
self.screen = AppScreen::GameDetail(Box::new(GameDetailScreen::new(
primary,
others,
GameDetailPrevious::Search(s),
self.downloads.shared(),
)));
self.maybe_start_game_detail_cover_load();
self.refresh_current_game_saves();
}
}
} else {
let query = search.query.clone();
let req = GetRoms {
search_term: Some(query.clone()),
limit: Some(50),
..Default::default()
};
search.loading = true;
if let Some(task) = self.search_load_task.take() {
task.abort();
}
let client = self.client.clone();
let tx = self.search_load_tx.clone();
self.search_load_task = Some(tokio::spawn(async move {
let mut req = req;
let mut aggregated: Option<RomList> = None;
loop {
match client.call(&req).await {
Ok(mut batch) => {
if let Some(ref mut all) = aggregated {
if batch.items.is_empty() {
break;
}
all.items.append(&mut batch.items);
let _ = tx.send(SearchLoadDone {
query: query.clone(),
event: SearchLoadEvent::Batch(all.clone()),
});
if all.items.len() as u64 >= all.total {
break;
}
req.offset = Some(all.items.len() as u32);
} else {
let loaded = batch.items.len() as u64;
let total = batch.total;
let _ = tx.send(SearchLoadDone {
query: query.clone(),
event: SearchLoadEvent::Batch(batch.clone()),
});
req.offset = Some(loaded as u32);
aggregated = Some(batch);
if loaded >= total {
break;
}
}
}
Err(e) => {
let _ = tx.send(SearchLoadDone {
query: query.clone(),
event: SearchLoadEvent::Failed(format!("{e:#}")),
});
return;
}
}
}
let _ = tx.send(SearchLoadDone {
query,
event: SearchLoadEvent::Complete,
});
}));
}
}
KeyCode::Esc => {
if search.results.is_some() {
search.clear_results();
} else {
self.screen = AppScreen::MainMenu(MainMenuScreen::new());
}
}
_ => {}
}
Ok(false)
}
}