trnovel 0.18.3

Terminal reader for novel
use crate::components::{ActionBar, ActionButton};
use crossterm::event::{Event, KeyCode, KeyEventKind};
use ratatui::layout::Direction;
use ratatui_kit::{
    AnyElement, EventPriority, EventResult, EventScope, Hooks, State, UseContext, UseEventHandler,
    UseState, component, element, prelude::View,
};

mod import_book_source;
mod select_book_source;
use import_book_source::ImportBookSource;
use select_book_source::SelectBookSource;

use crate::book_source::BookSourceCache;
use crate::components::modal::shortcut_info_modal::ShortcutInfoModal;

#[component]
pub fn BookSourceManager(mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
    let book_source_cache = *hooks.use_context::<State<Option<BookSourceCache>>>();

    let mut only_select = hooks.use_state(|| {
        book_source_cache
            .read()
            .as_ref()
            .is_some_and(|c| !c.book_sources.is_empty())
    });
    let mut info_modal_open = hooks.use_state(|| false);

    let mut browser_allowed = hooks.use_state(crate::browser_assist::always_allowed);
    let mut toggle_browser = move || {
        let on = !crate::browser_assist::always_allowed();
        if crate::browser_assist::set_always_allowed(on).is_ok() {
            browser_allowed.set(on);
        }
    };
    hooks.use_event_handler(EventScope::Current, EventPriority::Normal, move |event| {
        let Event::Key(key) = event else {
            return EventResult::Ignored;
        };
        if key.kind != KeyEventKind::Press {
            return EventResult::Ignored;
        }
        match key.code {
            KeyCode::Char('i') | KeyCode::Char('I') => {
                info_modal_open.set(!info_modal_open.get());
                EventResult::Consumed
            }
            KeyCode::Tab => {
                only_select.set(!only_select.get());
                EventResult::Consumed
            }
            // 反爬:开/关「浏览器辅助验证」总是允许(读 flag 翻转并持久化)。
            // 用 W(Web 浏览器)而非 B:B 是 shell 级后退键(`layout.rs`),同层里页面 Normal
            // 会先于 shell 的 Low 跑并 Consumed,占用 B 会让本页**彻底无法后退**。
            KeyCode::Char('w') | KeyCode::Char('W') => {
                toggle_browser();
                EventResult::Consumed
            }
            _ => EventResult::Ignored,
        }
    });

    element!(View {
        ActionBar(menu: "书源") {
            ActionButton(label: if only_select.get() { "导入书源" } else { "返回书源列表" }, shortcut: "Tab", on_press: move |_| only_select.set(!only_select.get()))
            ActionButton(label: if browser_allowed.get() { "浏览器辅助:开" } else { "浏览器辅助:关" }, shortcut: "W", on_press: move |_| toggle_browser())
            ActionButton(label: "帮助", shortcut: "I", on_press: move |_| info_modal_open.set(true))
        }
        { if only_select.get() {
        element!(View(
            flex_direction:Direction::Horizontal
        ){
            SelectBookSource(
                is_editing: !info_modal_open.get(),
            )
            ShortcutInfoModal(
                key_shortcut_info: vec![
                    ("切换导入书源模式", "Tab"),
                    ("删除书源", "D"),
                    ("书源登录(需登录的源)", "L"),
                    ("上下移动", "J / K / ↑ / ↓"),
                    ("选择书源", "Enter"),
                    ("浏览器辅助验证 开/关", "W"),
                ],
                open: info_modal_open.get(),
                on_close:{let mut open = info_modal_open; move |_: ()| open.set(false)},
            )
        }).into_any()
    } else {
        element!(View(
            flex_direction:Direction::Horizontal
        ){
            ImportBookSource(
                is_editing: !info_modal_open.get(),
            )
            SelectBookSource(
                is_editing: false,
            )
            ShortcutInfoModal(
                key_shortcut_info: vec![
                    ("切换仅选择模式", "Tab"),
                    ("输入书源地址", "S"),
                    ("选择/取消条目", "空格"),
                    ("确认导入/选择", "Enter"),
                    ("上下移动", "J / K / ↑ / ↓"),
                    ("浏览器辅助验证 开/关", "W"),
                ],
                open: info_modal_open.get(),
                on_close:{let mut open = info_modal_open; move |_: ()| open.set(false)},
            )
        }).into_any()
    } }
    })
}