1mod anime;
2pub mod common;
3mod display_block;
4mod help;
5mod input;
6mod option;
7mod user;
8use crate::api::model::{AnimeRankingType, MangaRankingType, Media};
9use crate::app::{
10 ActiveBlock, ActiveDisplayBlock, App, Data, SelectedSearchTab, TopThreeBlock,
11 ANIME_OPTIONS_RANGE, GENERAL_OPTIONS_RANGE, USER_OPTIONS_RANGE,
12};
13use crate::event::Key;
14use crate::network::IoEvent;
15
16use common::get_lowercase_key;
17pub use input::handler as input_handler;
18use log::warn;
19
20pub fn handle_app(key: Key, app: &mut App) {
21 if app.exit_confirmation_popup {
23 if key == Key::Esc || key == Key::Char('n') {
24 app.exit_confirmation_popup = false;
25 return;
26 } else if key == Key::Enter || get_lowercase_key(key) == Key::Char('y') {
27 app.exit_flag = true;
28 return;
29 }
30 }
31 match key {
32 Key::Esc => app.load_previous_route(),
33
34 _ if key == app.app_config.keys.next_state => app.load_next_route(),
35
36 _ if key == app.app_config.keys.help => {
37 app.active_display_block = ActiveDisplayBlock::Help;
38 }
39
40 _ if key == app.app_config.keys.search => {
41 app.input = vec![];
42 app.input_idx = 0;
43 app.input_cursor_position = 0;
44 app.active_block = ActiveBlock::Input;
45 }
46
47 _ => handle_block_events(key, app),
48 }
49}
50
51fn handle_block_events(key: Key, app: &mut App) {
53 let current_block = app.active_block;
54 match current_block {
55 ActiveBlock::Input => input::handler(key, app),
56
57 ActiveBlock::Anime => anime::handler(key, app),
58
59 ActiveBlock::User => user::handler(key, app),
60
61 ActiveBlock::Option => option::handler(key, app),
62
63 ActiveBlock::Error => {}
64
65 ActiveBlock::TopThree => display_block::top_three::handler(key, app),
66
67 ActiveBlock::DisplayBlock => display_block::handle_display_block(key, app),
68 }
69}
70
71pub fn handle_tab(app: &mut App) {
72 match app.active_block {
73 ActiveBlock::Input => {
74 app.library.selected_index = ANIME_OPTIONS_RANGE.start;
76 app.active_block = ActiveBlock::Anime;
77 }
78
79 ActiveBlock::Anime => {
80 app.library.selected_index = USER_OPTIONS_RANGE.start;
81 app.active_block = ActiveBlock::User;
82 }
83
84 ActiveBlock::User => {
85 app.library.selected_index = GENERAL_OPTIONS_RANGE.start;
86 app.active_block = ActiveBlock::Option;
87 }
88
89 ActiveBlock::Option => {
90 app.library.selected_index = 10; app.active_block = ActiveBlock::TopThree;
92 }
93
94 ActiveBlock::TopThree => {
95 app.active_block = ActiveBlock::DisplayBlock;
96 }
97
98 ActiveBlock::DisplayBlock => {
99 if !app.popup {
100 app.active_block = ActiveBlock::Input;
101 }
102 }
103 _ => {}
104 }
105}
106
107pub fn handle_back_tab(app: &mut App) {
108 match app.active_block {
109 ActiveBlock::Input => {
110 app.library.selected_index = 10; app.active_block = ActiveBlock::DisplayBlock;
112 }
113
114 ActiveBlock::DisplayBlock => {
115 if !app.popup {
116 app.active_block = ActiveBlock::TopThree;
117 }
118 }
119
120 ActiveBlock::TopThree => {
121 app.library.selected_index = GENERAL_OPTIONS_RANGE.end;
122 app.active_block = ActiveBlock::Option;
123 }
124
125 ActiveBlock::Option => {
126 app.library.selected_index = USER_OPTIONS_RANGE.end;
127 app.active_block = ActiveBlock::User;
128 }
129
130 ActiveBlock::User => {
131 app.library.selected_index = ANIME_OPTIONS_RANGE.end;
132 app.active_block = ActiveBlock::Anime;
133 }
134
135 ActiveBlock::Anime => {
136 app.library.selected_index = 10; app.active_block = ActiveBlock::Input;
138 }
139 _ => {}
140 }
141}
142
143pub fn is_data_available(
144 app: &App,
145 data: &Data,
146 block: ActiveDisplayBlock,
147) -> (bool, Option<usize>) {
148 for (i, route) in app.navigator.data.iter().enumerate() {
149 if route.1.block == block
150 && route.1.data.is_some()
151 && std::mem::discriminant(data)
152 == std::mem::discriminant(route.1.data.as_ref().unwrap())
153 {
154 return (true, Some(i));
155 }
156 }
157 (false, None)
158}
159
160pub fn get_media_detail_page(app: &mut App) {
161 let index = app.search_results.selected_display_card_index.unwrap_or(0)
162 + app.start_card_list_index as usize;
163
164 match app.active_block {
165 ActiveBlock::DisplayBlock => {
166 match app.active_display_block {
167 ActiveDisplayBlock::AnimeRanking => {
168 let data = app.anime_ranking_data.as_ref().unwrap().data.get(index);
169
170 if let Some(data) = data {
171 let (is_data_available, is_next, index) =
172 is_media_data_available(app, &Media::Anime(&data.node));
173 if is_next {
174 app.load_next_route();
175 return;
176 }
177 if is_data_available {
178 app.load_route(index.unwrap());
179 } else {
180 app.active_display_block = ActiveDisplayBlock::Loading;
181 app.dispatch(IoEvent::GetAnime(data.node.id));
182 }
183 app.active_block = ActiveBlock::DisplayBlock;
184 }
185 }
186
187 ActiveDisplayBlock::MangaRanking => {
188 let data = app.manga_ranking_data.as_ref().unwrap().data.get(index);
189
190 if let Some(data) = data {
191 let (is_data_available, is_next, index) =
192 is_media_data_available(app, &Media::Manga(&data.node));
193 if is_next {
194 app.load_next_route();
195 return;
196 }
197 if is_data_available {
198 app.load_route(index.unwrap());
199 } else {
200 app.active_display_block = ActiveDisplayBlock::Loading;
201 app.dispatch(IoEvent::GetManga(data.node.id));
202 }
203 app.active_block = ActiveBlock::DisplayBlock;
204 }
205 }
206
207 ActiveDisplayBlock::SearchResultBlock => match app.search_results.selected_tab {
208 SelectedSearchTab::Anime => {
209 let data = app.search_results.anime.as_ref().unwrap().data.get(index);
210
211 if let Some(data) = data {
212 let (is_data_available, is_next, index) =
213 is_media_data_available(app, &Media::Anime(&data.node));
214 if is_next {
215 app.load_next_route();
216 return;
217 }
218 if is_data_available {
219 app.load_route(index.unwrap());
220 } else {
221 app.active_display_block = ActiveDisplayBlock::Loading;
222 app.dispatch(IoEvent::GetAnime(data.node.id));
223 }
224 app.active_block = ActiveBlock::DisplayBlock;
225 }
226 }
227 SelectedSearchTab::Manga => {
228 let data = app.search_results.manga.as_ref().unwrap().data.get(index);
229
230 if let Some(data) = data {
231 let (is_data_available, is_next, index) =
232 is_media_data_available(app, &Media::Manga(&data.node));
233 if is_next {
234 app.load_next_route();
235 return;
236 }
237 if is_data_available {
238 app.load_route(index.unwrap());
239 } else {
240 app.active_display_block = ActiveDisplayBlock::Loading;
241 app.dispatch(IoEvent::GetManga(data.node.id));
242 }
243 app.active_block = ActiveBlock::DisplayBlock;
244 }
245 }
246 },
247
248 ActiveDisplayBlock::Suggestions => {
249 let data = app.search_results.anime.as_ref().unwrap().data.get(index);
250
251 if let Some(data) = data {
252 let (is_data_available, is_next, index) =
253 is_media_data_available(app, &Media::Anime(&data.node));
254 if is_next {
255 app.load_next_route();
256 return;
257 }
258 if is_data_available {
259 app.load_route(index.unwrap());
260 } else {
261 app.active_display_block = ActiveDisplayBlock::Loading;
262 app.dispatch(IoEvent::GetAnime(data.node.id));
263 }
264 app.active_block = ActiveBlock::DisplayBlock;
265 }
266 }
267
268 ActiveDisplayBlock::UserAnimeList => {
269 let data = app.search_results.anime.as_ref().unwrap().data.get(index);
270
271 if let Some(data) = data {
272 let (is_data_available, is_next, index) =
273 is_media_data_available(app, &Media::Anime(&data.node));
274 if is_next {
275 app.load_next_route();
276 return;
277 }
278 if is_data_available {
279 app.load_route(index.unwrap());
280 } else {
281 app.active_display_block = ActiveDisplayBlock::Loading;
282 app.dispatch(IoEvent::GetAnime(data.node.id));
283 }
284 app.active_block = ActiveBlock::DisplayBlock;
285 }
286 }
287
288 ActiveDisplayBlock::UserMangaList => {
289 let data = app.search_results.manga.as_ref().unwrap().data.get(index);
290
291 if let Some(data) = data {
292 let (is_data_available, is_next, index) =
293 is_media_data_available(app, &Media::Manga(&data.node));
294 if is_next {
295 app.load_next_route();
296 return;
297 }
298 if is_data_available {
299 app.load_route(index.unwrap());
300 } else {
301 app.active_display_block = ActiveDisplayBlock::Loading;
302 app.dispatch(IoEvent::GetManga(data.node.id));
303 }
304 app.active_block = ActiveBlock::DisplayBlock;
305 }
306 }
307
308 ActiveDisplayBlock::Seasonal => {
309 let data = app.search_results.anime.as_ref().unwrap().data.get(index);
310
311 if let Some(data) = data {
312 let (is_data_available, is_next, index) =
313 is_media_data_available(app, &Media::Anime(&data.node));
314 if is_next {
315 app.load_next_route();
316 return;
317 }
318 if is_data_available {
319 app.load_route(index.unwrap());
320 } else {
321 app.active_display_block = ActiveDisplayBlock::Loading;
322 app.dispatch(IoEvent::GetAnime(data.node.id));
323 }
324 app.active_block = ActiveBlock::DisplayBlock;
325 }
326 }
327
328 _ => {}
329 };
330 }
331 ActiveBlock::TopThree => match &app.active_top_three {
332 TopThreeBlock::Anime(anime_ranking_type) => {
333 let index = app.selected_top_three as usize % 3;
334 let mut anime = None;
335 match anime_ranking_type {
336 AnimeRankingType::Airing => {
337 if let Some(data) = &app.top_three_anime.airing {
338 anime = Some(&data[index]);
339 }
340 }
341 AnimeRankingType::All => {
342 if let Some(data) = &app.top_three_anime.all {
343 anime = Some(&data[index]);
344 }
345 }
346
347 AnimeRankingType::Upcoming => {
348 if let Some(data) = &app.top_three_anime.upcoming {
349 anime = Some(&data[index]);
350 }
351 }
352 AnimeRankingType::Favorite => {
353 if let Some(data) = &app.top_three_anime.favourite {
354 anime = Some(&data[index]);
355 }
356 }
357 AnimeRankingType::Movie => {
358 if let Some(data) = &app.top_three_anime.movie {
359 anime = Some(&data[index]);
360 }
361 }
362
363 AnimeRankingType::OVA => {
364 if let Some(data) = &app.top_three_anime.ova {
365 anime = Some(&data[index]);
366 }
367 }
368 AnimeRankingType::TV => {
369 if let Some(data) = &app.top_three_anime.tv {
370 anime = Some(&data[index]);
371 }
372 }
373 AnimeRankingType::ByPopularity => {
374 if let Some(data) = &app.top_three_anime.popular {
375 anime = Some(&data[index]);
376 }
377 }
378 AnimeRankingType::Special => {
379 if let Some(data) = &app.top_three_anime.special {
380 anime = Some(&data[index]);
381 }
382 }
383 AnimeRankingType::Other(_) => {
384 warn!("other anime ranking type was specified")
385 }
386 }
387 let anime = match anime {
388 Some(data) => data,
389 None => {
390 app.api_error = "Error: Not Found".to_string();
392 app.active_display_block = ActiveDisplayBlock::Error;
393 return;
394 }
395 };
396 let (is_data_available, is_next, index) =
397 is_media_data_available(app, &Media::Anime(anime));
398 if is_next {
399 app.load_next_route();
400 return;
401 }
402 if is_data_available {
403 app.load_route(index.unwrap());
404 } else {
405 app.active_display_block = ActiveDisplayBlock::Loading;
406 app.dispatch(IoEvent::GetAnime(anime.id));
407 }
408 }
409 TopThreeBlock::Manga(manga_ranking_type) => {
410 let index = app.selected_top_three as usize % 3;
411 let mut manga = None;
412 match manga_ranking_type {
413 MangaRankingType::All => {
414 if let Some(data) = &app.top_three_manga.all {
415 manga = Some(&data[index]);
416 }
417 }
418 MangaRankingType::Manga => {
419 if let Some(data) = &app.top_three_manga.manga {
420 manga = Some(&data[index]);
421 }
422 }
423 MangaRankingType::OneShots => {
424 if let Some(data) = &app.top_three_manga.oneshots {
425 manga = Some(&data[index]);
426 }
427 }
428 MangaRankingType::ByPopularity => {
429 if let Some(data) = &app.top_three_manga.popular {
430 manga = Some(&data[index]);
431 }
432 }
433 MangaRankingType::Favorite => {
434 if let Some(data) = &app.top_three_manga.favourite {
435 manga = Some(&data[index]);
436 }
437 }
438 MangaRankingType::Manhua => {
439 if let Some(data) = &app.top_three_manga.manhua {
440 manga = Some(&data[index]);
441 }
442 }
443 MangaRankingType::Manhwa => {
444 if let Some(data) = &app.top_three_manga.manhwa {
445 manga = Some(&data[index]);
446 }
447 }
448 MangaRankingType::Novels => {
449 if let Some(data) = &app.top_three_manga.novels {
450 manga = Some(&data[index]);
451 }
452 }
453 MangaRankingType::Doujinshi => {
454 if let Some(data) = &app.top_three_manga.doujin {
455 manga = Some(&data[index]);
456 }
457 }
458 MangaRankingType::Other(_) => {
459 warn!("other manga ranking type was specified")
460 } }
462
463 let manga = match manga {
464 Some(data) => data,
465 None => {
466 app.api_error = "Error: Not Found".to_string();
468 app.active_display_block = ActiveDisplayBlock::Error;
469 return;
470 }
471 };
472
473 let (is_data_available, is_next, index) =
474 is_media_data_available(app, &Media::Manga(manga));
475 if is_next {
476 app.load_next_route();
477 return;
478 }
479 if is_data_available {
480 app.load_route(index.unwrap());
481 } else {
482 app.active_display_block = ActiveDisplayBlock::Loading;
483 app.dispatch(IoEvent::GetManga(manga.id));
484 }
485 }
486 _ => {}
487 },
488 _ => {}
489 }
490}
491
492fn is_media_data_available(app: &App, data: &Media) -> (bool, bool, Option<u16>) {
493 match data {
494 Media::Anime(data) => {
495 for i in 0..(app.navigator.history.len()) {
496 let page_id = app.navigator.history[i];
497 if app.navigator.data[&page_id].block == ActiveDisplayBlock::AnimeDetails
498 && app.navigator.data[&page_id].data.is_some()
499 {
500 if let Data::Anime(d) = app.navigator.data[&page_id].data.as_ref().unwrap() {
501 if d.id == data.id {
502 let is_next = app.navigator.index + 1 == i;
503 return (true, is_next, Some(page_id));
504 }
505 }
506 }
507 }
508 }
509 Media::Manga(data) => {
510 for i in 0..(app.navigator.history.len()) {
511 let id = app.navigator.history[i];
512 if app.navigator.data[&id].block == ActiveDisplayBlock::MangaDetails
513 && app.navigator.data[&id].data.is_some()
514 {
515 if let Data::Manga(d) = app.navigator.data[&id].data.as_ref().unwrap() {
516 if d.id == data.id {
517 let is_next = app.navigator.index + 1 == i;
518 return (true, is_next, Some(id));
519 }
520 }
521 }
522 }
523 }
524 }
525 (false, false, None)
526}