diff --git a/src/data_provider/card_sets.rs b/src/data_provider/card_sets.rs index 13aa4db..6b36f36 100644 --- a/src/data_provider/card_sets.rs +++ b/src/data_provider/card_sets.rs @@ -1,21 +1,25 @@ +use crate::lang::SetOrderMode; use crate::repetitions::CardSetSettings; use rusqlite::Connection; -use crate::lang::SetOrderMode; pub fn load_sets(connection: &Connection) -> Vec { - let mut stmt = connection.prepare("SELECT id, name, forward, backward, filter FROM card_set").unwrap(); - let iter = stmt.query_map([], |row| { - Ok(CardSetSettings { - id: row.get(0)?, - name: row.get(1)?, - forward: row.get(2)?, - backward: row.get(3)?, - filter: row.get(4)?, - count: None, - worst_words_list: None, - open_mode: SetOrderMode::Default, + let mut stmt = connection + .prepare("SELECT id, name, forward, backward, filter FROM card_set") + .unwrap(); + let iter = stmt + .query_map([], |row| { + Ok(CardSetSettings { + id: row.get(0)?, + name: row.get(1)?, + forward: row.get(2)?, + backward: row.get(3)?, + filter: row.get(4)?, + count: None, + worst_words_list: None, + open_mode: SetOrderMode::Default, + }) }) - }).unwrap(); + .unwrap(); let mut buffer = vec![]; for word in iter { @@ -42,12 +46,10 @@ pub fn add_set(set: &mut CardSetSettings, connection: &Connection) { set.id = index; } -pub fn update_card_set(set: &mut CardSetSettings, connection: &Connection){ +pub fn update_card_set(set: &mut CardSetSettings, connection: &Connection) { if set.id == 0 { add_set(set, &connection); - } - - else { + } else { connection .execute( "UPDATE card_set SET name = ?1, forward = ?2, backward = ?3, filter = ?4 WHERE id = ?5", @@ -73,4 +75,4 @@ pub fn delete_set(set: &CardSetSettings, connection: &Connection) { println!("{}", e); 0 }); -} \ No newline at end of file +} diff --git a/src/data_provider/card_stats.rs b/src/data_provider/card_stats.rs index 179db16..febab17 100644 --- a/src/data_provider/card_stats.rs +++ b/src/data_provider/card_stats.rs @@ -4,16 +4,20 @@ use rusqlite::Connection; use std::time::Instant; pub fn load_stats_of_set(set: &CardSetSettings, connection: &Connection) -> Vec { - let mut stmt = connection.prepare("SELECT id, word_id, score, last_opened FROM card_stats WHERE set_id = ?1").unwrap(); - let iter = stmt.query_map((set.id,), |row| { - Ok(CardStatistics { - id: row.get(0)?, - word_id: row.get(1)?, - set_id: set.id, - score: row.get(2)?, - last_open: row.get(3)?, + let mut stmt = connection + .prepare("SELECT id, word_id, score, last_opened FROM card_stats WHERE set_id = ?1") + .unwrap(); + let iter = stmt + .query_map((set.id,), |row| { + Ok(CardStatistics { + id: row.get(0)?, + word_id: row.get(1)?, + set_id: set.id, + score: row.get(2)?, + last_open: row.get(3)?, + }) }) - }).unwrap(); + .unwrap(); let mut buffer = vec![]; for word in iter { @@ -23,23 +27,38 @@ pub fn load_stats_of_set(set: &CardSetSettings, connection: &Connection) -> Vec< buffer } - pub fn add_stat_list(stat: &mut Vec, connection: &Connection) { - let inserting = stat.iter().map(|stat| format!("({}, {}, {}, {})", stat.word_id.to_string(), stat.set_id.to_string(), stat.score.to_string(), stat.last_open.timestamp().to_string())).collect::>().join(", "); - let query = format!("INSERT INTO card_stats (word_id, set_id, score, last_opened) VALUES {}", inserting); - let count = connection - .execute( - query.as_str(), - ( - ), - ); + let inserting = stat + .iter() + .map(|stat| { + format!( + "({}, {}, {}, {})", + stat.word_id.to_string(), + stat.set_id.to_string(), + stat.score.to_string(), + stat.last_open.timestamp().to_string() + ) + }) + .collect::>() + .join(", "); + let query = format!( + "INSERT INTO card_stats (word_id, set_id, score, last_opened) VALUES {}", + inserting + ); + let count = connection.execute(query.as_str(), ()); if count.is_err() { println!("{}", count.unwrap_err()); return; } - let last_index : u32 = connection.query_one("SELECT seq from sqlite_sequence WHERE name == ?1", ("card_stats".to_string(),), |row| row.get(0)).unwrap(); + let last_index: u32 = connection + .query_one( + "SELECT seq from sqlite_sequence WHERE name == ?1", + ("card_stats".to_string(),), + |row| row.get(0), + ) + .unwrap(); let start_index = last_index - (count.unwrap() as u32) + 1; @@ -48,10 +67,8 @@ pub fn add_stat_list(stat: &mut Vec, connection: &Connection) { stat[index].id = id as u32; index += 1; } - } - pub fn add_stat(stat: &mut CardStatistics, connection: &Connection) { let time = Instant::now(); let index = connection @@ -71,21 +88,19 @@ pub fn add_stat(stat: &mut CardStatistics, connection: &Connection) { println!("Added stat: {}", time.elapsed().as_millis()); } -pub fn update_stat_score(stat: &CardStatistics, connection: &Connection){ +pub fn update_stat_score(stat: &CardStatistics, connection: &Connection) { let time = Instant::now(); - connection - .execute( - "UPDATE card_stats SET score = ?1, last_opened = ?2 WHERE id = ?3", - ( - &stat.score, - &stat.last_open.timestamp(), - &stat.id - ), - ) - .unwrap_or_else(|e| {println!("{}", e); 0}); + connection + .execute( + "UPDATE card_stats SET score = ?1, last_opened = ?2 WHERE id = ?3", + (&stat.score, &stat.last_open.timestamp(), &stat.id), + ) + .unwrap_or_else(|e| { + println!("{}", e); + 0 + }); println!("Updated stat: {}", time.elapsed().as_millis()); - } pub fn delete_stat(stat: &CardStatistics, connection: &Connection) { diff --git a/src/data_provider/history.rs b/src/data_provider/history.rs index fd98c88..2d31971 100644 --- a/src/data_provider/history.rs +++ b/src/data_provider/history.rs @@ -33,7 +33,6 @@ fn parse_history_items(strings: Vec) -> Vec { let mut items = Vec::with_capacity(strings.len()); for string in strings { if let [time, word, mode, before, after] = string.split(';').collect::>()[..] { - items.push(HistoryItem { timestamp: DateTime::from_timestamp(time.parse().unwrap(), 0).unwrap(), word_id: word.parse::().unwrap(), @@ -55,16 +54,25 @@ fn parse_history_items(strings: Vec) -> Vec { pub fn push_note(set_id: u32, item: HistoryItem) { let app_dir = app_data_dir(); let path = app_dir.clone().join(format!("set_{}_history.csv", set_id)); - - let mut file = OpenOptions::new().create(true).append(true).open(path).unwrap(); - let line_str = format!("{};{};{};{};{}", item.timestamp.timestamp(), item.word_id, match item.mode { - WordOpenMode::Easy => 4, - WordOpenMode::Ok => 3, - WordOpenMode::Hard => 2, - WordOpenMode::None => 1 - }, - item.before, - item.after); + + let mut file = OpenOptions::new() + .create(true) + .append(true) + .open(path) + .unwrap(); + let line_str = format!( + "{};{};{};{};{}", + item.timestamp.timestamp(), + item.word_id, + match item.mode { + WordOpenMode::Easy => 4, + WordOpenMode::Ok => 3, + WordOpenMode::Hard => 2, + WordOpenMode::None => 1, + }, + item.before, + item.after + ); writeln!(&mut file, "{}", line_str.to_string()).unwrap(); } diff --git a/src/data_provider/mod.rs b/src/data_provider/mod.rs index 4f8b99f..274a229 100644 --- a/src/data_provider/mod.rs +++ b/src/data_provider/mod.rs @@ -1,8 +1,7 @@ -pub(crate) mod words; pub(crate) mod card_sets; pub(crate) mod card_stats; -pub(crate) mod voice; +pub(crate) mod history; pub(crate) mod settings; pub(crate) mod sqlite; -pub(crate) mod history; - +pub(crate) mod voice; +pub(crate) mod words; diff --git a/src/data_provider/settings.rs b/src/data_provider/settings.rs index f2acf24..9a26c25 100644 --- a/src/data_provider/settings.rs +++ b/src/data_provider/settings.rs @@ -1,10 +1,10 @@ use rusqlite::Connection; pub fn get_setting(key: String, connection: &Connection) -> Option { - let mut stmt = connection.prepare("SELECT value FROM settings WHERE id = ?1").unwrap(); - let iter = stmt.query_map((key,), |row| { - row.get(0) - }).unwrap(); + let mut stmt = connection + .prepare("SELECT value FROM settings WHERE id = ?1") + .unwrap(); + let iter = stmt.query_map((key,), |row| row.get(0)).unwrap(); for row in iter { if let Ok(value) = row { @@ -19,7 +19,7 @@ pub fn set_setting(key: String, value: String, connection: &Connection) { let current = get_settings_list(connection); if current.contains(&key) { update_settings(key, value, connection); - }else { + } else { create_settings(key, value, connection); } } @@ -56,13 +56,11 @@ where id = ?1", .unwrap_or_else(|e| { println!("{}", e); 0 - });} + }); +} fn get_settings_list(connection: &Connection) -> Vec { let mut stmt = connection.prepare("SELECT id FROM settings").unwrap(); - let iter = stmt.query_map((), |row| { - row.get(0) - }).unwrap(); - iter.map(|row| { row.unwrap() }).collect() + let iter = stmt.query_map((), |row| row.get(0)).unwrap(); + iter.map(|row| row.unwrap()).collect() } - diff --git a/src/data_provider/sqlite.rs b/src/data_provider/sqlite.rs index dc9488d..0cd0b1a 100644 --- a/src/data_provider/sqlite.rs +++ b/src/data_provider/sqlite.rs @@ -10,16 +10,14 @@ pub fn create_db() { connection.execute("PRAGMA foreign_keys = ON;", []).unwrap(); create_tables(&connection); - }else { + } else { let connection = Connection::open(&db_file).unwrap(); ensure_db_schema(&connection); } } -fn ensure_db_schema(conn: &Connection) { - -} +fn ensure_db_schema(conn: &Connection) {} fn create_tables(conn: &Connection) { make_card_set(conn).unwrap(); diff --git a/src/data_provider/voice.rs b/src/data_provider/voice.rs index 8ef9e26..45a6833 100644 --- a/src/data_provider/voice.rs +++ b/src/data_provider/voice.rs @@ -27,9 +27,11 @@ pub async fn get_voice(text: &str) -> BufReader { .header("Content-Type", "application/json") .body(query) .send() - .await.unwrap() + .await + .unwrap() .bytes() - .await.unwrap(); + .await + .unwrap(); tokio::fs::write(&path, &audio).await.unwrap(); } diff --git a/src/data_provider/words.rs b/src/data_provider/words.rs index 871b26f..24a5e36 100644 --- a/src/data_provider/words.rs +++ b/src/data_provider/words.rs @@ -2,7 +2,6 @@ use crate::lang::{WordData, WordGroup}; use rusqlite::Connection; use std::collections::HashMap; - pub fn add_word(word: &mut WordData, connection: &Connection) { let index = connection .query_row( diff --git a/src/dictionary.rs b/src/dictionary.rs index 4781f13..96d54e6 100644 --- a/src/dictionary.rs +++ b/src/dictionary.rs @@ -12,6 +12,7 @@ use iced::alignment::Vertical::Center; use iced::widget::button::Style; use iced::widget::button::{danger, text}; use iced::widget::space::horizontal; +use iced::widget::text_input::default; use iced::widget::*; use iced::{Border, Color, Length, Shadow, Task}; use rand::random_range; @@ -21,8 +22,6 @@ use std::ops::Add; use std::path::PathBuf; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; -use iced::widget::text_input::default; -use DictionaryMessage::Back; #[derive(Clone)] pub struct DictionaryState { @@ -295,12 +294,10 @@ impl DictionaryState { iced::widget::column![ self.groups_panel(), self.words_list(), - button("Добавить слово") - .style(jl_button) - .on_press(NewWord), - ].spacing(5), + button("Добавить слово").style(jl_button).on_press(NewWord), + ] + .spacing(5), self.filters(), - ] .spacing(5) .into(), @@ -340,10 +337,7 @@ impl DictionaryState { let mut line = Row::new().width(Length::Fill).align_y(Center); line = line - .push( - checkbox(self.include_map[i]) - .on_toggle(move |b| Include(i, b)), - ) + .push(checkbox(self.include_map[i]).on_toggle(move |b| Include(i, b))) .push(space().width(10)); line = line.push( @@ -396,9 +390,7 @@ impl DictionaryState { }); } - button("") - .on_press(WordAction(i)) - .width(15) + button("").on_press(WordAction(i)).width(15) }; line = line.push(line_button()).push(space().width(10)); @@ -524,11 +516,7 @@ impl DictionaryState { fn groups_panel(&self) -> iced::Element<'_, DictionaryMessage> { let mut row = Row::new(); - row = row.push( - button("+") - .style(text) - .on_press(CreateGroup), - ); + row = row.push(button("+").style(text).on_press(CreateGroup)); let state = &self.state.lock().unwrap(); let groups = &state.word_groups; diff --git a/src/dictionary_test.rs b/src/dictionary_test.rs index bf95585..eda22f3 100644 --- a/src/dictionary_test.rs +++ b/src/dictionary_test.rs @@ -1,17 +1,17 @@ -use crate::dictionary::{split_with_coma}; +use crate::dictionary::split_with_coma; +use crate::dictionary_test::DictionaryQuizMessage::*; +use crate::lang::WordData; +use crate::navigation::Page::PreviousPage; +use crate::navigation::*; use crate::quiz::Score; -use crate::{RootMessage}; +use crate::styling::*; +use crate::RootMessage; use iced::border::Radius; use iced::widget::container::Style; use iced::widget::{button, container, row, space, text, text_input, Row}; use iced::Background::Color; use iced::{alignment, Border, Element, Fill, Task, Theme}; use rand::prelude::SliceRandom; -use crate::dictionary_test::DictionaryQuizMessage::*; -use crate::lang::WordData; -use crate::navigation::{NavigatedPage, Page}; -use crate::navigation::Page::PreviousPage; -use crate::styling::*; #[derive(Debug, Clone)] pub struct DictionaryQuizState { @@ -44,11 +44,7 @@ impl NavigatedPage for DictionaryQuizState { } impl DictionaryQuizState { - pub fn new( - words: Vec, - reverse: bool, - no_typing: bool, - ) -> DictionaryQuizState { + pub fn new(words: Vec, reverse: bool, no_typing: bool) -> DictionaryQuizState { DictionaryQuizState { words, current_set: Vec::new(), @@ -86,7 +82,9 @@ impl DictionaryQuizState { } else { String::new() } - ).size(ACCENT_FONT_SIZE).align_y(alignment::Vertical::Center), + ) + .size(ACCENT_FONT_SIZE) + .align_y(alignment::Vertical::Center), ] .align_x(alignment::Horizontal::Center) .spacing(5), @@ -136,7 +134,7 @@ impl DictionaryQuizState { self.is_help = false; self.score.total += 1; self.show_next() - }else { + } else { self.is_help = true; } } @@ -206,9 +204,7 @@ impl DictionaryQuizState { fn appeal_button(&self) -> Element<'_, DictionaryQuizMessage> { if self.is_help && self.no_typing == false { - return button("Апелляция").style(jl_button) - .on_press(Appeal) - .into(); + return button("Апелляция").style(jl_button).on_press(Appeal).into(); } space().into() } diff --git a/src/history.rs b/src/history.rs index 62a173b..b1214b9 100644 --- a/src/history.rs +++ b/src/history.rs @@ -51,26 +51,19 @@ impl HistoryState { } pub fn view(&self) -> Element<'_, HistoryMessage> { - back_overlay(iced::widget::row![ - horizontal().width(FillPortion(1)), - scrollable(self.history_lines().padding(DEFAULT_SPACING)) - .height(Fill) - .width(FillPortion(5)), - horizontal().width(FillPortion(1)) - ] - .height(Fill) - .width(Fill).into(), Back) - // container( - // iced::widget::column![ - // button("Назад").style(jl_button).on_press(Back), - // - // ] - // .align_x(Left) - // .width(Fill), - // ) - // .center_x(Fill) - // .padding(10) - // .into() + back_overlay( + iced::widget::row![ + horizontal().width(FillPortion(1)), + scrollable(self.history_lines().padding(DEFAULT_SPACING)) + .height(Fill) + .width(FillPortion(5)), + horizontal().width(FillPortion(1)) + ] + .height(Fill) + .width(Fill) + .into(), + Back, + ) } fn history_lines(&self) -> Column<'_, HistoryMessage> { diff --git a/src/lang.rs b/src/lang.rs index 3688f22..da47709 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -1,12 +1,12 @@ -use crate::AppState; use crate::data_provider::card_stats::{ add_stat_list, delete_stat, load_stats_of_set, update_stat_score, }; -use crate::data_provider::history::{HistoryItem, push_note}; +use crate::data_provider::history::{push_note, HistoryItem}; use crate::repetitions::CardSetSettings; +use crate::AppState; use chrono::{DateTime, Utc}; -use rand::distr::Distribution; use rand::distr::weighted::WeightedIndex; +use rand::distr::Distribution; use rand::prelude::SliceRandom; use rand::rng; use rand::rngs::ThreadRng; @@ -399,13 +399,13 @@ impl CardSet { index } OrderModule::WorstWordsSRS(mut module) => { - if module.initialized == false { - module.init(self) - } - let index = module.next(self); - self.order_module = OrderModule::WorstWordsSRS(module); - index - } + if module.initialized == false { + module.init(self) + } + let index = module.next(self); + self.order_module = OrderModule::WorstWordsSRS(module); + index + } }; self.current_word_index = Some(index); @@ -433,9 +433,9 @@ impl CardSet { self.order_module = OrderModule::RandomSRS(module); } OrderModule::WorstWordsSRS(mut module) => { - module.open(status, index, word.clone()); - self.order_module = OrderModule::WorstWordsSRS(module); - } + module.open(status, index, word.clone()); + self.order_module = OrderModule::WorstWordsSRS(module); + } } update_stat_score(word, &self.state.lock().unwrap().connection); push_note( @@ -596,9 +596,7 @@ impl SRSModule for WorstWordsSRSModule { self.queue.pop().unwrap() } - - fn open(&mut self, _: WordOpenMode, _: usize, _: CardStatistics) { - } + fn open(&mut self, _: WordOpenMode, _: usize, _: CardStatistics) {} fn init(&mut self, _: &mut CardSet) { self.initialized = true; @@ -618,9 +616,17 @@ impl WorstWordsSRSModule { } fn fill_pool(&mut self, set: &CardSet) { - let mut sorted = set.set.clone().into_iter().zip(0..set.set.len()).collect::>(); + let mut sorted = set + .set + .clone() + .into_iter() + .zip(0..set.set.len()) + .collect::>(); sorted.sort_by_key(|c| c.0.score); - let mut worst = sorted[0..self.pool_size].iter().map(|(_, index)| *index).collect::>(); + let mut worst = sorted[0..self.pool_size] + .iter() + .map(|(_, index)| *index) + .collect::>(); worst.shuffle(&mut rand::rng()); self.pool = worst; } diff --git a/src/main.rs b/src/main.rs index 1e5f17f..a60f5b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,18 +2,18 @@ mod data_provider; mod dictionary; mod dictionary_test; +mod history; mod lang; +pub mod navigation; mod quiz; mod randomizer; mod repetition; mod repetitions; mod selector; +pub mod styling; mod sync; mod word; mod writing; -mod history; -pub mod navigation; -pub mod styling; use crate::data_provider::card_sets::load_sets; use crate::data_provider::settings::get_setting; @@ -28,15 +28,13 @@ use iced::Font; use iced::{keyboard, Program, Subscription, Theme}; use rusqlite::Connection; - const USER_FONT: Font = Font::with_name("Noto Sans JP"); fn main() -> iced::Result { - iced::application(ScreenState::boot, ScreenState::update, ScreenState::view) .subscription(subscription) .title("Kana learn app") - .settings(iced::Settings{ + .settings(iced::Settings { default_text_size: iced::Pixels(18.0), ..iced::Settings::default() }) @@ -50,8 +48,6 @@ fn subscription(_state: &ScreenState) -> Subscription { keyboard::listen().map(|e| Keyboard(e)) } - - pub struct AppState { pub dictionary: Vec, pub card_sets: Vec, @@ -67,7 +63,7 @@ impl AppState { let connection = Connection::open(db_file).unwrap(); connection.execute("PRAGMA foreign_keys = ON;", []).unwrap(); - Self{ + Self { dictionary: vec![], card_sets: vec![], word_groups: vec![], @@ -77,8 +73,6 @@ impl AppState { } } - - fn fill_state(state: &mut AppState) { let list = load_words(&state.connection); let sets = load_sets(&state.connection); @@ -95,4 +89,3 @@ fn load_settings(connection: &Connection) -> AppSettings { let key = get_setting("SYNC_KEY".to_string(), connection); AppSettings { key } } - diff --git a/src/navigation.rs b/src/navigation.rs index f192fa4..900113b 100644 --- a/src/navigation.rs +++ b/src/navigation.rs @@ -1,25 +1,24 @@ -use crate::message_navigation; -use crate::state_update; -use crate::view_navigation; -use std::sync::{Arc, Mutex}; -use iced::{Element, Task}; -use iced::keyboard::Event; -use crate::{fill_state, AppState }; use crate::data_provider::sqlite::create_db; use crate::dictionary::{DictionaryMessage, DictionaryState}; use crate::dictionary_test::{DictionaryQuizMessage, DictionaryQuizState}; use crate::history::{HistoryMessage, HistoryState}; +use crate::message_navigation; use crate::navigation::Page::*; use crate::navigation::RootMessage::Keyboard; use crate::quiz::{QuizMessage, QuizState}; -use crate::randomizer::randomizer::{RandomizerMessage, RandomizerState}; +use crate::randomizer::{RandomizerMessage, RandomizerState}; use crate::repetition::{RepetitionMessage, RepetitionState}; use crate::repetitions::{RepetitionsMessage, RepetitionsState}; use crate::selector::{SelectorMessage, SelectorState}; +use crate::state_update; use crate::sync::{SyncMessage, SyncState}; +use crate::view_navigation; use crate::word::{WordMessage, WordState}; use crate::writing::{WritingMessage, WritingState}; - +use crate::{fill_state, AppState}; +use iced::keyboard::Event; +use iced::{Element, Task}; +use std::sync::{Arc, Mutex}; impl Default for ScreenState { fn default() -> Self { @@ -63,9 +62,6 @@ pub enum Page { PreviousPage, } - - - pub struct ScreenState { stack: Vec, } diff --git a/src/quiz.rs b/src/quiz.rs index baf0ee8..a27b7d2 100644 --- a/src/quiz.rs +++ b/src/quiz.rs @@ -1,12 +1,12 @@ use crate::lang::KanaSet; -use crate::{ RootMessage, USER_FONT}; +use crate::navigation::Page::PreviousPage; +use crate::navigation::{NavigatedPage, Page}; +use crate::quiz::QuizMessage::*; +use crate::styling::*; +use crate::{RootMessage, USER_FONT}; use iced::widget::*; use iced::{alignment, Element, Fill, Task}; use rand::seq::SliceRandom; -use crate::navigation::{NavigatedPage, Page}; -use crate::navigation::Page::PreviousPage; -use crate::quiz::QuizMessage::*; -use crate::styling::*; #[derive(Clone, Debug)] pub struct QuizState { @@ -104,7 +104,9 @@ impl QuizState { container( iced::widget::column![ row![ - text!("{}", self.kana.to_uppercase()).size(54).font(USER_FONT), + text!("{}", self.kana.to_uppercase()) + .size(54) + .font(USER_FONT), text!( "{}", if self.is_help { diff --git a/src/randomizer.rs b/src/randomizer.rs index 5300412..c4808f7 100644 --- a/src/randomizer.rs +++ b/src/randomizer.rs @@ -1,82 +1,80 @@ -pub mod randomizer { - use crate::navigation::{NavigatedPage, Page}; - use crate::randomizer::randomizer::RandomizerMessage::{Back, Edit, Start}; - use crate::styling::{back_overlay, jl_button}; - use crate::RootMessage; - use iced::widget::{button, text_editor}; - use iced::Task; - use rand::prelude::SliceRandom; +use crate::navigation::{NavigatedPage, Page}; +use crate::randomizer::RandomizerMessage::{Back, Edit, Start}; +use crate::styling::{back_overlay, jl_button}; +use crate::RootMessage; +use iced::widget::{button, text_editor}; +use iced::Task; +use rand::prelude::SliceRandom; - #[derive(Clone, Debug)] - pub struct RandomizerState { - text: text_editor::Content, - list: Vec, - } +#[derive(Clone, Debug)] +pub struct RandomizerState { + text: text_editor::Content, + list: Vec, +} - #[derive(Debug, Clone)] - pub enum RandomizerMessage { - Back, - Start, - Edit(text_editor::Action), - } +#[derive(Debug, Clone)] +pub enum RandomizerMessage { + Back, + Start, + Edit(text_editor::Action), +} - impl NavigatedPage for RandomizerState { - fn navigate(&self, message: &RandomizerMessage) -> Option { - if let Back = message { - return Some(Page::PreviousPage); - } - None - } - } - - impl Default for RandomizerState { - fn default() -> Self { - Self::new() - } - } - - impl RandomizerState { - pub fn new() -> RandomizerState { - RandomizerState { - text: Default::default(), - list: vec![], - } - } - - pub fn update(&mut self, message: RandomizerMessage) -> Task { - match message { - Edit(action) => { - self.text.perform(action); - self.list = self - .text - .text() - .split("\n") - .map(|s| s.to_string()) - .collect(); - } - Start => { - self.list.shuffle(&mut rand::rng()); - self.text = text_editor::Content::with_text(self.list.join("\n").as_str()); - } - _ => {} - } - Task::none() - } - - pub fn view(&self) -> iced::Element<'_, RandomizerMessage> { - back_overlay( - iced::widget::column![ - text_editor(&self.text) - .on_action(Edit) - .width(400) - .height(400) - .placeholder("Каждый элемент с новой строки"), - button("Перемешать").style(jl_button).on_press(Start), - ] - .spacing(5) - .into(), - Back, - ) +impl NavigatedPage for RandomizerState { + fn navigate(&self, message: &RandomizerMessage) -> Option { + if let Back = message { + return Some(Page::PreviousPage); } + None + } +} + +impl Default for RandomizerState { + fn default() -> Self { + Self::new() + } +} + +impl RandomizerState { + pub fn new() -> RandomizerState { + RandomizerState { + text: Default::default(), + list: vec![], + } + } + + pub fn update(&mut self, message: RandomizerMessage) -> Task { + match message { + Edit(action) => { + self.text.perform(action); + self.list = self + .text + .text() + .split("\n") + .map(|s| s.to_string()) + .collect(); + } + Start => { + self.list.shuffle(&mut rand::rng()); + self.text = text_editor::Content::with_text(self.list.join("\n").as_str()); + } + _ => {} + } + Task::none() + } + + pub fn view(&self) -> iced::Element<'_, RandomizerMessage> { + back_overlay( + iced::widget::column![ + text_editor(&self.text) + .on_action(Edit) + .width(400) + .height(400) + .placeholder("Каждый элемент с новой строки"), + button("Перемешать").style(jl_button).on_press(Start), + ] + .spacing(5) + .into(), + Back, + ) } } diff --git a/src/repetition.rs b/src/repetition.rs index e4373e4..b940b19 100644 --- a/src/repetition.rs +++ b/src/repetition.rs @@ -113,31 +113,34 @@ impl RepetitionState { } pub fn view(&self) -> Element<'_, RepetitionMessage> { - back_overlay(column![ - container(self.draw_forward()) - .width(Fill) - .height(Fill) - .align_x(Center) - .align_y(alignment::Vertical::Center), - rule::horizontal(2), - container(self.draw_backward()) - .width(Fill) - .height(Fill) - .align_x(Center) - .align_y(alignment::Vertical::Center), - container(self.answer_bar()) - .width(Fill) - .align_x(Center) - .height(80), - text!( - "Затронуто слов {}, {}%", - self.opened.len(), - (self.opened.len() as f32 / self.set.len() as f32 * 10000.0).round() - / 100.0 - ) - ] + back_overlay( + column![ + container(self.draw_forward()) + .width(Fill) + .height(Fill) + .align_x(Center) + .align_y(alignment::Vertical::Center), + rule::horizontal(2), + container(self.draw_backward()) + .width(Fill) + .height(Fill) + .align_x(Center) + .align_y(alignment::Vertical::Center), + container(self.answer_bar()) + .width(Fill) + .align_x(Center) + .height(80), + text!( + "Затронуто слов {}, {}%", + self.opened.len(), + (self.opened.len() as f32 / self.set.len() as f32 * 10000.0).round() / 100.0 + ) + ] .height(Fill) - .width(Fill).into(), RepetitionMessage::Back) + .width(Fill) + .into(), + RepetitionMessage::Back, + ) } fn draw_forward(&self) -> Element<'_, RepetitionMessage> { diff --git a/src/repetitions.rs b/src/repetitions.rs index 53c0a65..4782a91 100644 --- a/src/repetitions.rs +++ b/src/repetitions.rs @@ -10,7 +10,9 @@ use crate::{AppState, RootMessage}; use iced::widget::button::{danger, Status}; pub use iced::widget::button::{Catalog, Style}; use iced::widget::container::bordered_box; -use iced::widget::{button, column, container, radio, row, scrollable, space, text, text_input, Column}; +use iced::widget::{ + button, column, container, radio, row, scrollable, space, text, text_input, Column, +}; use iced::{Background, Border, Center, Color, Element, Fill, Length, Shadow, Task, Theme}; use rhai::{Engine, Scope}; use std::sync::{Arc, Mutex}; @@ -19,7 +21,7 @@ use std::sync::{Arc, Mutex}; pub struct RepetitionsState { selected_set: Option, correct_filters: Vec, - + pub state: Arc>, } @@ -111,36 +113,48 @@ impl RepetitionsState { let set = state.card_sets.get(self.selected_set.unwrap()).unwrap(); let count = set.get_word_list(&state).len(); state.card_sets[self.selected_set.unwrap()].count = Some(count); - }, + } RepetitionsMessage::SetOpenMode(mode) => { - state.card_sets.get_mut(self.selected_set.unwrap()).unwrap().open_mode = mode; + state + .card_sets + .get_mut(self.selected_set.unwrap()) + .unwrap() + .open_mode = mode; } } Task::none() } pub fn view(&self) -> Element<'_, RepetitionsMessage> { - back_overlay(row![ - column![ - scrollable(self.sets_list()).height(Fill), - button("Добавить").style(jl_button) - .width(Fill) - .on_press(RepetitionsMessage::CreateSet), - ] - .spacing(DEFAULT_SPACING) - .width(Length::FillPortion(1)), - self.selected_set_view(), - self.launch_button() + back_overlay( + row![ + column![ + scrollable(self.sets_list()).height(Fill), + button("Добавить") + .style(jl_button) + .width(Fill) + .on_press(RepetitionsMessage::CreateSet), ] + .spacing(DEFAULT_SPACING) + .width(Length::FillPortion(1)), + self.selected_set_view(), + self.launch_button() + ] .align_y(Center) .spacing(DEFAULT_SPACING) .width(Fill) - .height(Fill).into(), RepetitionsMessage::Back) + .height(Fill) + .into(), + RepetitionsMessage::Back, + ) } fn launch_button(&self) -> Element<'_, RepetitionsMessage> { - if let Some(set) = self.selected_set && self.state.lock().unwrap().card_sets[set].id != 0 { - return button(text!("▷").height(Fill).center()).style(jl_button) + if let Some(set) = self.selected_set + && self.state.lock().unwrap().card_sets[set].id != 0 + { + return button(text!("▷").height(Fill).center()) + .style(jl_button) .height(200) .on_press(RepetitionsMessage::GoToRepetition) .into(); @@ -165,17 +179,28 @@ impl RepetitionsState { .on_input(RepetitionsMessage::SetBackward), text!("Фильтр"), text_input("", &set.filter).on_input(RepetitionsMessage::SetFilter), - button("Проверить фильтр").style(jl_button).on_press(RepetitionsMessage::TryFilter), + button("Проверить фильтр") + .style(jl_button) + .on_press(RepetitionsMessage::TryFilter), self.count_view(&set), - radio("Обычный режим", SetOrderMode::Default, Some(set.open_mode), RepetitionsMessage::SetOpenMode), + radio( + "Обычный режим", + SetOrderMode::Default, + Some(set.open_mode), + RepetitionsMessage::SetOpenMode + ), self.words_words_view(&set), - button("История").style(jl_button).on_press(RepetitionsMessage::GoToHistory), + button("История") + .style(jl_button) + .on_press(RepetitionsMessage::GoToHistory), ] .spacing(DEFAULT_SPACING) ) .height(Fill), row![ - button("Сохранить").style(jl_button).on_press(RepetitionsMessage::Save), + button("Сохранить") + .style(jl_button) + .on_press(RepetitionsMessage::Save), button("Удалить") .style(danger) .on_press(RepetitionsMessage::DeleteSet) @@ -193,16 +218,24 @@ impl RepetitionsState { column![ text!("Худшие слова"), container(scrollable(self.worst_words_list(&set)).height(200)).style(bordered_box), - radio("Начать с плохих слов", SetOrderMode::TrainWorstFirst, Some(set.open_mode), RepetitionsMessage::SetOpenMode), - radio("Полностью случайно", SetOrderMode::FullRandom, Some(set.open_mode), RepetitionsMessage::SetOpenMode) + radio( + "Начать с плохих слов", + SetOrderMode::TrainWorstFirst, + Some(set.open_mode), + RepetitionsMessage::SetOpenMode + ), + radio( + "Полностью случайно", + SetOrderMode::FullRandom, + Some(set.open_mode), + RepetitionsMessage::SetOpenMode + ) ] .spacing(DEFAULT_SPACING) .into() } fn worst_words_list(&self, set: &CardSetSettings) -> Element<'_, RepetitionsMessage> { - - let mut column = Column::new(); for word in set.worst_words_list.clone().unwrap() { @@ -226,14 +259,18 @@ impl RepetitionsState { column = column.push( button(text!("{}", set.name.clone())) .on_press_with(move || RepetitionsMessage::SelectSet(i.clone())) - .style(move |_x: &Theme, status : Status| Style { - background: if status == Status::Hovered {Some(Background::Color(Color::WHITE.scale_alpha(0.1)))} else { None }, + .style(move |_x: &Theme, status: Status| Style { + background: if status == Status::Hovered { + Some(Background::Color(Color::WHITE.scale_alpha(0.1))) + } else { + None + }, text_color: if self.correct_filters[i.clone()] { _x.palette().primary } else { _x.palette().warning }, - border: Border{ + border: Border { color: Default::default(), width: 0.0, radius: 8.0.into(), @@ -247,7 +284,6 @@ impl RepetitionsState { column } - } #[derive(Clone)] @@ -277,7 +313,7 @@ pub struct CardSetSettings { pub filter: String, pub count: Option, pub worst_words_list: Option>, - pub open_mode: SetOrderMode + pub open_mode: SetOrderMode, } impl CardSetSettings { @@ -346,7 +382,7 @@ impl CardSetSettings { self.forward == "speech" || self.backward == "speech" } - fn update_worst_words(&mut self, state: &AppState){ + fn update_worst_words(&mut self, state: &AppState) { if let Some(_) = self.worst_words_list { return; } @@ -360,15 +396,14 @@ impl CardSetSettings { .iter() .take_while(|word| word.calculated_score() < avg) .map(|stat| { - state.dictionary[ state + state.dictionary[state .dictionary .binary_search_by_key(&stat.word_id, |x| x.id) - .unwrap()].clone() + .unwrap()] + .clone() }) .collect(); self.worst_words_list = Some(bad.clone()); } - - } diff --git a/src/selector.rs b/src/selector.rs index 8f8474e..81e9759 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -1,22 +1,22 @@ -use std::sync::{Arc, Mutex}; use crate::dictionary::DictionaryState; use crate::lang::{KanaSet, KanaType}; -use crate::randomizer::randomizer::RandomizerState; +use crate::navigation::Page::*; +use crate::navigation::{NavigatedPage, Page}; +use crate::randomizer::RandomizerState; use crate::repetitions::RepetitionsState; use crate::selector::SelectorMessage::ChangeMode; +use crate::styling::*; +use crate::sync::SyncState; use crate::writing::WritingState; use crate::{AppState, QuizState, RootMessage}; use iced::widget::*; use iced::{alignment, Element, Task}; -use crate::navigation::{NavigatedPage, Page}; -use crate::navigation::Page::*; -use crate::styling::*; -use crate::sync::SyncState; +use std::sync::{Arc, Mutex}; pub struct SelectorState { pub set: KanaSet, is_writing: bool, - state: Arc> + state: Arc>, } #[derive(Debug, Clone)] @@ -61,7 +61,7 @@ impl NavigatedPage for SelectorState { impl SelectorState { pub fn new(state: Arc>) -> Self { - Self{ + Self { set: Default::default(), is_writing: false, state, @@ -84,18 +84,30 @@ impl SelectorState { container( iced::widget::column![ row![ - button("あ ↔ ア").on_press(SelectorMessage::Change).style(jl_button), - button("Словарь").on_press(SelectorMessage::ToDictionary).style(button::text), - button("Рандомайзер").on_press(SelectorMessage::ToRandomize).style(button::text), - button("Повторение").on_press(SelectorMessage::ToRepetitions).style(button::text), - button("Синхронизация").on_press(SelectorMessage::ToSync).style(button::text) + button("あ ↔ ア") + .on_press(SelectorMessage::Change) + .style(jl_button), + button("Словарь") + .on_press(SelectorMessage::ToDictionary) + .style(button::text), + button("Рандомайзер") + .on_press(SelectorMessage::ToRandomize) + .style(button::text), + button("Повторение") + .on_press(SelectorMessage::ToRepetitions) + .style(button::text), + button("Синхронизация") + .on_press(SelectorMessage::ToSync) + .style(button::text) ] .spacing(DEFAULT_SPACING), self.rows_selector(), toggler(self.is_writing) .label("Режим письма") .on_toggle(ChangeMode), - button("К тесту").on_press(SelectorMessage::Goto).style(jl_button), + button("К тесту") + .on_press(SelectorMessage::Goto) + .style(jl_button), ] .spacing(DEFAULT_SPACING), ) diff --git a/src/sync.rs b/src/sync.rs index 6baeea8..137df0b 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -15,7 +15,6 @@ use std::time::Duration; use zstd::{Decoder, Encoder, DEFAULT_COMPRESSION_LEVEL}; const API_URL: &str = "https://learning.micialware.ru/"; -/*const API_URL: &str = "http://localhost:8089/";*/ #[derive(Clone)] pub enum SyncMessage { @@ -70,14 +69,11 @@ impl SyncState { .map(|_val: String| RootMessage::Sync(KeyCopied)); } InitSync => { - return Task::perform(first_sync(), |id| { - RootMessage::Sync(IdReceived(id)) - }); + return Task::perform(first_sync(), |id| RootMessage::Sync(IdReceived(id))); } GetKey => { - return iced::clipboard::read().map(|key| { - RootMessage::Sync(IdReceived(key.unwrap_or_else(String::new))) - }); + return iced::clipboard::read() + .map(|key| RootMessage::Sync(IdReceived(key.unwrap_or_else(String::new)))); } KeyCopied => {} IdReceived(new_id) => { @@ -97,9 +93,7 @@ impl SyncState { async { tokio::time::sleep(Duration::from_millis(200)).await }, |_| RootMessage::Sync(NextAnimation), ), - Task::perform(send_data(id), |_| { - RootMessage::Sync(NetworkFinished) - }), + Task::perform(send_data(id), |_| RootMessage::Sync(NetworkFinished)), ]); return tasks; @@ -111,9 +105,7 @@ impl SyncState { async { tokio::time::sleep(Duration::from_millis(200)).await }, |_| RootMessage::Sync(NextAnimation), ), - Task::perform(load_data(id), |_| { - RootMessage::Sync(NetworkFinished) - }), + Task::perform(load_data(id), |_| RootMessage::Sync(NetworkFinished)), ]); return tasks; @@ -141,7 +133,6 @@ impl SyncState { state.card_sets = updated_state.card_sets; state.dictionary = updated_state.dictionary; state.word_groups = updated_state.word_groups; - } Disable => {} DisableSync => { @@ -155,9 +146,13 @@ impl SyncState { } pub fn view(&self) -> Element<'_, SyncMessage> { - back_overlay(row![space().width(Fill), self.sync_column(), space().width(Fill)] - .spacing(DEFAULT_SPACING) - .width(Fill).into(), Back) + back_overlay( + row![space().width(Fill), self.sync_column(), space().width(Fill)] + .spacing(DEFAULT_SPACING) + .width(Fill) + .into(), + Back, + ) } fn sync_column(&self) -> Element<'_, SyncMessage> { @@ -171,11 +166,16 @@ impl SyncState { column![ text!("Ваш ключ синхронизации"), container( - container(text!("{}", key).size(ACCENT_FONT_SIZE).font(Font::MONOSPACE)).padding(3) + container( + text!("{}", key) + .size(ACCENT_FONT_SIZE) + .font(Font::MONOSPACE) + ) + .padding(3) ) - .style(rounded_box), - button("Скопировать в буфер обмена").style(jl_button) + button("Скопировать в буфер обмена") + .style(jl_button) .on_press(CopyKey) .width(Fill), row![ @@ -191,18 +191,22 @@ impl SyncState { ] } else { column![ - button("Создать сохранение").style(jl_button).on_press(InitSync), - button("Вставить ключ из буфера").style(jl_button).on_press(GetKey), + button("Создать сохранение") + .style(jl_button) + .on_press(InitSync), + button("Вставить ключ из буфера") + .style(jl_button) + .on_press(GetKey), ] } } .spacing(DEFAULT_SPACING) .align_x(Center) - .width(Length::Shrink) + .width(Length::Shrink) .into() } -/* fn app_updater(&self) -> Element<'_, SyncMessage> { + /* fn app_updater(&self) -> Element<'_, SyncMessage> { column![].width(Fill).into() }*/ diff --git a/src/writing.rs b/src/writing.rs index 8b220f1..a6ccaf2 100644 --- a/src/writing.rs +++ b/src/writing.rs @@ -1,11 +1,11 @@ use crate::lang::KanaSet; -use crate::{ RootMessage}; +use crate::navigation::Page::PreviousPage; +use crate::navigation::{NavigatedPage, Page}; +use crate::styling::*; +use crate::RootMessage; use iced::widget::*; use iced::{alignment, Element, Fill, Task}; use rand::seq::SliceRandom; -use crate::navigation::{NavigatedPage, Page}; -use crate::navigation::Page::PreviousPage; -use crate::styling::*; #[derive(Clone, Debug)] pub struct WritingState { @@ -45,7 +45,7 @@ impl WritingState { } impl WritingState { - pub fn update(&mut self, message: WritingMessage) -> Task { + pub fn update(&mut self, message: WritingMessage) -> Task { match message { WritingMessage::Back => todo!(), WritingMessage::Next => self.next(), @@ -66,7 +66,6 @@ impl WritingState { } if self.show_all { - if self.set.is_empty() == false && self.kana_total.is_empty() == false { self.set.clear(); } @@ -75,8 +74,6 @@ impl WritingState { self.roman_total += &*format!("{} ", &pair.1.clone()).to_string(); self.kana_total += &*format!("{} ", &pair.0).to_string(); } - - } else { let current = self.set.pop().unwrap(); self.kana_total += &*format!("{} ", ¤t.0).to_string(); @@ -95,8 +92,12 @@ impl WritingState { text!("{}", self.kana).size(48), self.answers(), row![ - button(text!("{}", self.next_text)).style(jl_button).on_press(WritingMessage::Next), - button("Закончить").style(jl_button).on_press(WritingMessage::Back), + button(text!("{}", self.next_text)) + .style(jl_button) + .on_press(WritingMessage::Next), + button("Закончить") + .style(jl_button) + .on_press(WritingMessage::Back), ] .spacing(DEFAULT_SPACING) ] @@ -110,7 +111,7 @@ impl WritingState { } fn answers(&self) -> Element<'_, WritingMessage> { - if self.set.is_empty() { + if self.set.is_empty() { text!("{}", self.kana_total).size(36).into() } else { space().height(36).into()