use crate::components::modal::shortcut_info_modal::ShortcutInfoModal;
use crate::{
components::{WarningModal, select::Select},
errors::Errors,
hooks::{UseInitState, UseThemeConfig},
};
use crossterm::event::{Event, KeyCode, KeyEventKind};
use parse_book_source::{BookSource, Category, Engine};
use ratatui::{
layout::{Constraint, Margin},
style::{Style, Stylize},
text::Line,
widgets::{ListItem, ListState},
};
use ratatui_kit::prelude::*;
use std::hash::Hash;
mod find_book;
use find_book::*;
#[derive(Debug, Clone, Hash)]
pub struct ExploreListItem(pub Category);
impl From<ExploreListItem> for ListItem<'_> {
fn from(value: ExploreListItem) -> Self {
ListItem::new(value.0.title.clone())
}
}
#[component]
pub fn SelectBooks(mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
let mut info_modal_open = hooks.use_state(|| false);
let is_inputting = *hooks.use_context::<State<bool>>();
let book_source = hooks.use_route_state::<BookSource>();
let mut explores = hooks.use_state(std::vec::Vec::new);
let theme = hooks.use_theme_config();
let list_state = hooks.use_state(ListState::default);
let mut is_explore_open = hooks.use_state(|| false);
let mut current_explore = hooks.use_state(|| None::<ExploreListItem>);
let (book_source_engine, _, error) = hooks.use_init_state(async move {
let engine = crate::browser_assist::build_engine((*book_source).clone())?;
engine.warmup().await;
let book_source_explores = engine.explore_categories();
if let Some(explore) = book_source_explores.first() {
current_explore.set(Some(ExploreListItem(explore.clone())));
list_state.write().select_first();
}
explores.set(book_source_explores);
Ok::<Engine, Errors>(engine)
});
hooks.use_events(move |event| {
if let Event::Key(key) = event
&& key.kind == KeyEventKind::Press
&& !is_inputting.get()
{
match key.code {
KeyCode::Tab => {
is_explore_open.set(!is_explore_open.get());
}
KeyCode::Char('i') | KeyCode::Char('I') => {
info_modal_open.set(!info_modal_open.get());
}
_ => {}
}
}
});
let explores_list = hooks.use_memo(
move || {
explores
.read()
.iter()
.map(|item| ExploreListItem(item.clone()))
.collect::<Vec<_>>()
},
explores.read().len(),
);
element!(View {
FindBooks(..FindBooksProps {
engine: book_source_engine,
current_explore: current_explore.read().clone(),
is_editing: !is_explore_open.get() && !info_modal_open.get(),
}
)
WarningModal(
tip: format!("{:?}", error.read().as_ref()),
is_error: error.read().is_some(),
open: error.read().is_some(),
)
ShortcutInfoModal(
key_shortcut_info: vec![
("切换分类弹窗", "Tab"),
("上下移动", "J / K / ↑ / ↓"),
("选择/进入", "Enter"),
("上一页", "H / ←"),
("下一页", "L / →"),
("搜索书籍", "S"),
],
open: info_modal_open.get(),
)
Modal(
width: Constraint::Length(30),
height: Constraint::Percentage(70),
style: Style::default().dim(),
open: is_explore_open.get(),
) {
View(
margin: Margin::new(1,1),
){
Select<ExploreListItem>(
items: explores_list.clone(),
state: list_state,
on_select: move |item:ExploreListItem| {
current_explore.set(Some(item.clone()));
is_explore_open.set(false);
},
top_title: Line::from("选择分类").style(theme.basic.border_title).centered(),
is_editing: is_explore_open.get(),
empty_message: "暂无分类".to_string(),
)
}
}
})
}