use ratatui_image::picker::Picker;
use ratatui_image::protocol::StatefulProtocol;
use tui_input::Input;
use crate::browser::Browser;
use crate::tui::settings::SettingsState;
use crate::tui::types::{
HistoryEntry, ImageResult, InputMode, InstantResult, NewsResult, RenderedPage, Tab, WebResult,
};
use crate::user_agents::get;
pub struct ImageViewerState {
pub title: String,
pub image_url: String,
pub page_url: String,
pub proto: StatefulProtocol,
}
pub struct App {
pub input_mode: InputMode,
pub selected_tab: Tab,
pub search_input: Input,
pub browser: Browser,
pub web_results: Vec<WebResult>,
pub image_results: Vec<ImageResult>,
pub news_results: Vec<NewsResult>,
pub instant_result: Option<InstantResult>,
pub selected_result: usize,
pub scroll: u16,
pub status: String,
pub page_view: Option<RenderedPage>,
pub history: Vec<HistoryEntry>,
pub history_forward: Vec<HistoryEntry>,
pub page_scroll: u16,
pub page_selected_link: usize,
pub settings: SettingsState,
pub settings_text_input: Input,
pub image_picker: Option<Picker>,
pub page_images: Vec<(String, StatefulProtocol)>,
pub image_viewer: Option<ImageViewerState>,
pub should_quit: bool,
}
impl Default for App {
fn default() -> Self {
Self::new(None)
}
}
impl App {
pub fn new(image_picker: Option<Picker>) -> Self {
Self {
input_mode: InputMode::Normal,
selected_tab: Tab::Web,
search_input: Input::default(),
browser: Browser::new(),
web_results: vec![],
image_results: vec![],
news_results: vec![],
instant_result: None,
selected_result: 0,
scroll: 0,
status: String::from(
"Ready | 'e' to search | Tab: switch | Enter: open | Backspace: back | 'q' quit",
),
page_view: None,
history: vec![],
history_forward: vec![],
page_scroll: 0,
page_selected_link: 0,
settings: SettingsState::default(),
settings_text_input: Input::default(),
image_picker,
page_images: vec![],
image_viewer: None,
should_quit: false,
}
}
pub fn is_page_open(&self) -> bool {
self.page_view.is_some()
}
pub fn has_image_viewer(&self) -> bool {
self.image_viewer.is_some()
}
pub fn close_image_viewer(&mut self) {
self.image_viewer = None;
}
pub fn close_page(&mut self) {
self.page_view = None;
self.page_scroll = 0;
self.page_selected_link = 0;
self.page_images.clear();
}
pub fn push_page(&mut self, page: RenderedPage) {
if let Some(current) = &self.page_view {
self.history.push(HistoryEntry {
url: current.url.clone(),
});
}
self.history_forward.clear();
self.page_scroll = 0;
self.page_selected_link = 0;
self.page_images.clear();
self.page_view = Some(page);
}
pub fn go_back(&mut self) -> Option<String> {
match self.history.pop() {
Some(entry) => {
if let Some(current) = &self.page_view {
self.history_forward.push(HistoryEntry {
url: current.url.clone(),
});
}
self.page_scroll = 0;
self.page_selected_link = 0;
self.page_images.clear();
Some(entry.url)
}
None => {
self.close_page();
None
}
}
}
pub fn go_forward(&mut self) -> Option<String> {
self.history_forward.pop().map(|entry| {
if let Some(current) = &self.page_view {
self.history.push(HistoryEntry {
url: current.url.clone(),
});
}
self.page_scroll = 0;
self.page_selected_link = 0;
self.page_images.clear();
entry.url
})
}
pub fn selected_web_url(&self) -> Option<&str> {
self.web_results
.get(self.selected_result)
.map(|r| r.url.as_str())
}
pub fn selected_news_url(&self) -> Option<&str> {
self.news_results
.get(self.selected_result)
.map(|r| r.url.as_str())
}
pub fn selected_page_link(&self) -> Option<&crate::tui::types::PageLink> {
self.page_view
.as_ref()
.and_then(|p| p.links.get(self.page_selected_link))
}
pub fn clamp_selected_result(&mut self) {
let max = match self.selected_tab {
Tab::Web => self.web_results.len(),
Tab::Images => self.image_results.len(),
Tab::News => self.news_results.len(),
Tab::Instant | Tab::Settings => 0,
};
if max == 0 {
self.selected_result = 0;
} else if self.selected_result >= max {
self.selected_result = max - 1;
}
}
pub fn apply_settings(&mut self) -> anyhow::Result<()> {
let mut builder = Browser::builder();
let ua = get(&self.settings.user_agent_name).unwrap_or("");
if !ua.is_empty() {
builder = builder.user_agent(ua);
}
if self.settings.cookie_store {
builder = builder.cookie_store(true);
}
if !self.settings.proxy.is_empty() {
builder = builder.proxy(&self.settings.proxy);
}
self.browser = builder.build()?;
Ok(())
}
}