feat: tabs, state management

This commit is contained in:
Artemy Egorov 2024-07-29 22:14:05 +03:00
parent 9dfd66c14a
commit 107a8245e4
12 changed files with 414 additions and 51 deletions

View file

@ -2,8 +2,7 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::{
fs::{self, File},
io::Write,
fs::{self},
sync::Mutex,
};
@ -13,30 +12,106 @@ mod types;
mod utils;
use tauri::Manager;
use types::{State, VigiError};
use utils::{read_jsonl_tabs, read_or_create_current_tab_index, read_or_create_jsonl};
use types::{TabType, VigiError, VigiState};
use utils::{read_or_create_jsonl, read_or_create_number};
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[tauri::command]
async fn process_input(input: String) -> Result<Vec<Tag>, VigiError> {
async fn process_input(
input: String,
state: tauri::State<'_, Mutex<VigiState>>,
) -> Result<Vec<Tag>, VigiError> {
// TODO: Implement mime type, language, protocol or search detection
// TODO: Implement text links parsing
match reqwest::get(input).await {
println!("Processing: {}", input);
match reqwest::get(input.clone()).await {
Ok(res) => match res.text().await {
Ok(res) => Ok(vec![Tag::new(0, Body::Text(res), Argument::Null)]),
Err(_) => Err(VigiError::ParseError),
Ok(res) => {
update_tab(state, TabType::Text, res.clone(), input.clone())?;
Ok(vec![Tag::new(0, Body::Text(res), Argument::Null)])
}
Err(_) => Err(VigiError::Parse),
},
Err(_) => Err(VigiError::NetworkError),
Err(_) => Err(VigiError::Network),
}
}
#[tauri::command]
fn get_state(state: tauri::State<Mutex<VigiState>>) -> VigiState {
(*state.lock().unwrap()).clone()
}
#[tauri::command]
fn select_tab(state: tauri::State<Mutex<VigiState>>, index: usize) -> Result<(), VigiError> {
match state.lock() {
Ok(mut state) => {
state.update_current_tab_index(index)?;
Ok(())
}
Err(_) => Err(VigiError::StateLock),
}
}
#[tauri::command]
fn add_tab(state: tauri::State<Mutex<VigiState>>) -> Result<(), VigiError> {
match state.lock() {
Ok(mut state) => {
state.add_tab()?;
Ok(())
}
Err(_) => Err(VigiError::StateLock),
}
}
#[tauri::command]
fn remove_tab(state: tauri::State<Mutex<VigiState>>, index: usize) -> Result<(), VigiError> {
match state.lock() {
Ok(mut state) => {
state.remove_tab(index)?;
Ok(())
}
Err(_) => Err(VigiError::StateLock),
}
}
fn update_tab(
state: tauri::State<Mutex<VigiState>>,
tab_type: TabType,
tab_title: String,
tab_url: String,
) -> Result<(), VigiError> {
match state.lock() {
Ok(mut state) => {
state.update_tab(tab_type, tab_title, tab_url)?;
Ok(())
}
Err(_) => Err(VigiError::StateLock),
}
}
fn main() {
tauri::Builder::default()
.manage(Mutex::new(VigiState::null()))
.setup(setup_handler)
.invoke_handler(tauri::generate_handler![
process_input,
get_state,
select_tab,
add_tab,
remove_tab
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
fn setup_handler(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error + 'static>> {
println!("---Setup---");
let app_handle = app.handle();
let state = app.state::<Mutex<State>>();
let state = app.state::<Mutex<VigiState>>();
let mut state = state.lock().unwrap();
let config_dir = app_handle
@ -78,19 +153,13 @@ fn setup_handler(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error +
state.local_tabs_path = local_data_dir.join("tabs.jsonl");
state.tabs = read_or_create_jsonl(&state.local_tabs_path);
state.tabs_id_counter_path = local_data_dir.join("tabs_id_counter");
state.tabs_id_counter = read_or_create_number(&state.tabs_id_counter_path);
state.current_tab_index_path = local_data_dir.join("current_tab_index");
state.current_tab_index = read_or_create_current_tab_index(&state.current_tab_index_path);
state.current_tab_index = read_or_create_number(&state.current_tab_index_path);
println!("---Setup done---");
Ok(())
}
fn main() {
tauri::Builder::default()
.manage(Mutex::new(State::null()))
.setup(setup_handler)
.invoke_handler(tauri::generate_handler![process_input])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View file

@ -1,47 +1,129 @@
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use std::{fs, path::PathBuf};
use crate::utils::write_tabs;
#[derive(Serialize, Deserialize, Debug)]
pub enum VigiError {
NetworkError,
ParseError,
Network,
Parse,
StateLock,
StateUpdate,
}
pub struct State {
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct VigiState {
pub tabs_id_counter_path: PathBuf,
pub current_tab_index_path: PathBuf,
pub local_tabs_path: PathBuf,
pub favorites_tabs_path: PathBuf,
pub cache_dir: PathBuf,
pub tabs_id_counter: usize,
pub current_tab_index: usize,
pub tabs: Vec<Tab>,
pub favorites_tabs: Vec<Tab>,
}
impl State {
impl VigiState {
pub fn null() -> Self {
Self {
tabs_id_counter_path: PathBuf::new(),
current_tab_index_path: PathBuf::new(),
local_tabs_path: PathBuf::new(),
favorites_tabs_path: PathBuf::new(),
cache_dir: PathBuf::new(),
tabs_id_counter: 0,
current_tab_index: 0,
tabs: Vec::new(),
favorites_tabs: Vec::new(),
}
}
pub fn update_current_tab_index(&mut self, new_index: usize) -> Result<(), VigiError> {
self.current_tab_index = new_index;
self.write_current_tab_index()?;
Ok(())
}
fn write_current_tab_index(&mut self) -> Result<(), VigiError> {
fs::write(
&self.current_tab_index_path,
self.current_tab_index.to_string(),
)
.map_err(|_| VigiError::StateUpdate)
}
fn write_id_counter(&mut self) -> Result<(), VigiError> {
fs::write(&self.tabs_id_counter_path, self.tabs_id_counter.to_string())
.map_err(|_| VigiError::StateUpdate)
}
pub fn update_tab(
&mut self,
tab_type: TabType,
tab_title: String,
tab_url: String,
) -> Result<(), VigiError> {
match self.tabs.get_mut(self.current_tab_index) {
Some(tab) => {
*tab = Tab::new(tab_type, tab_title, tab_url, tab.id);
write_tabs(&self.local_tabs_path, &self.tabs)?;
Ok(())
}
None => Err(VigiError::StateUpdate),
}
}
pub fn add_tab(&mut self) -> Result<(), VigiError> {
self.tabs_id_counter += 1;
self.tabs.push(Tab::new(
TabType::HomePage,
"New tab".to_string(),
"".to_string(),
self.tabs_id_counter,
));
self.write_id_counter()?;
write_tabs(&self.local_tabs_path, &self.tabs)?;
Ok(())
}
pub fn remove_tab(&mut self, index: usize) -> Result<(), VigiError> {
if self.tabs.len() == 1 {
self.current_tab_index = 0;
} else {
self.current_tab_index = self.current_tab_index + 1;
}
self.tabs.remove(index);
write_tabs(&self.local_tabs_path, &self.tabs)?;
self.write_current_tab_index()?;
Ok(())
}
}
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Tab {
ty: TabType,
name: String,
title: String,
url: String,
id: usize,
}
#[derive(Serialize, Deserialize, Debug)]
impl Tab {
pub fn new(ty: TabType, title: String, url: String, id: usize) -> Self {
Self { ty, title, url, id }
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum TabType {
HomePage,
Text,

View file

@ -3,7 +3,7 @@ use std::{
path::PathBuf,
};
use crate::types::Tab;
use crate::types::{Tab, VigiError};
pub fn read_jsonl_tabs(path: &PathBuf) -> Vec<Tab> {
fs::read_to_string(&path)
@ -24,11 +24,8 @@ pub fn read_or_create_jsonl(path: &PathBuf) -> Vec<Tab> {
}
}
pub fn read_or_create_current_tab_index(path: &PathBuf) -> usize {
println!(
" Getting current tab index from {}",
path.to_string_lossy()
);
pub fn read_or_create_number(path: &PathBuf) -> usize {
println!(" Getting number from {}", path.to_string_lossy());
if path.exists() {
fs::read_to_string(path)
.unwrap()
@ -40,3 +37,14 @@ pub fn read_or_create_current_tab_index(path: &PathBuf) -> usize {
0
}
}
pub fn write_tabs(path: &PathBuf, tabs: &Vec<Tab>) -> Result<(), VigiError> {
fs::write(
path,
tabs.iter()
.map(|tab| serde_json::to_string(tab).unwrap())
.collect::<Vec<String>>()
.join("\n"),
)
.map_err(|_| VigiError::StateUpdate)
}