use crate::data_provider::import::{load_groups, ImportData}; use crate::dictionary::app_data_dir; use crate::import::ImportMessage::*; use crate::navigation::{NavigatedPage, Page, RootMessage}; use crate::styling::*; use crate::AppState; use iced::widget::scrollable; use iced::widget::{button, column, row, text}; use iced::{Element, Task}; use iced_core::Alignment::Center; use rfd::AsyncFileDialog; use rusqlite::Connection; use std::fs::{File, OpenOptions}; use std::io::BufReader; use std::path::PathBuf; use std::sync::{Arc, Mutex}; use tokio::task::spawn_blocking; use zip::ZipArchive; #[derive(Clone)] pub struct ImportState { state: Arc>, path: Option, import_data: Option, selected_index: usize, } #[derive(Clone)] pub enum ImportMessage { Back, SelectFile, UpdateFile(PathBuf), UpdateImport(ImportData), } impl NavigatedPage for ImportState { fn navigate(&self, message: &ImportMessage) -> Option { if let Back = message { return Some(Page::PreviousPage); } None } fn navigated(&mut self) {} fn update(&mut self, message: ImportMessage) -> Task { match message { Back => {} UpdateFile(path) => { self.path = Some(path); return self.load_package(); } SelectFile => return Self::select_import_file(), UpdateImport(import) => { self.import_data = Some(import); } } Task::none() } fn view(&self) -> Element<'_, ImportMessage> { back_overlay( scrollable( column![ row![ button("Выбрать файл").style(jl_button).on_press(SelectFile), text!("{}", { if let Some(path) = &self.path { path.to_string_lossy().to_string() } else { "Файл не выбран".to_string() } }) ] .align_y(Center) .spacing(DEFAULT_SPACING) ] .spacing(DEFAULT_SPACING), ) .into(), Back, ) } } impl ImportState { pub fn new(state: Arc>) -> ImportState { ImportState { state, path: None, import_data: None, selected_index: 0 } } fn select_import_file() -> Task { Task::perform( async { let file = AsyncFileDialog::new() .add_filter("Anki", &["apkg"]) .pick_file() .await; if let Some(file) = file { return Some(file.path().into()); } None }, |result| { if let Some(path) = result { RootMessage::Import(UpdateFile(path)) } else { RootMessage::None } }, ) } pub fn load_package(&self) -> Task { let path = self.path.clone().unwrap(); Task::perform( async { spawn_blocking(move || { Self::extract_import_file(path)?; let data = Self::read_import_file()?; return Ok(data); }) .await .unwrap() }, |result: Result| { if let Ok(import) = result { RootMessage::Import(UpdateImport(import)) } else { RootMessage::None } }, ) } fn extract_import_file(path: PathBuf) -> Result<(), ()> { let file = File::open(path).map_err(|_| ())?; let reader = BufReader::new(file); let mut archive = ZipArchive::new(reader).map_err(|_| ())?; let db_file = archive.by_name("collection.anki2"); if let Ok(mut file) = db_file { let temp_file_path = app_data_dir().join("import"); let mut temp_file = OpenOptions::new() .write(true) .create(true) .truncate(true) .open(&temp_file_path) .map_err(|_| ())?; std::io::copy(&mut file, &mut temp_file).map_err(|_| ())?; Ok(()) } else { Err(()) } } fn read_import_file() -> Result { let temp_file_path = app_data_dir().join("import"); let connection = Connection::open(&temp_file_path).map_err(|_| ())?; let data = load_groups(&connection); Ok(data) } }