code cleanup

This commit is contained in:
2026-08-01 19:43:07 +03:00
parent 42d0488f23
commit 20cb50574c
21 changed files with 421 additions and 373 deletions
+10 -8
View File
@@ -1,10 +1,13 @@
use crate::lang::SetOrderMode;
use crate::repetitions::CardSetSettings;
use rusqlite::Connection;
use crate::lang::SetOrderMode;
pub fn load_sets(connection: &Connection) -> Vec<CardSetSettings> {
let mut stmt = connection.prepare("SELECT id, name, forward, backward, filter FROM card_set").unwrap();
let iter = stmt.query_map([], |row| {
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)?,
@@ -15,7 +18,8 @@ pub fn load_sets(connection: &Connection) -> Vec<CardSetSettings> {
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",
+37 -22
View File
@@ -4,8 +4,11 @@ use rusqlite::Connection;
use std::time::Instant;
pub fn load_stats_of_set(set: &CardSetSettings, connection: &Connection) -> Vec<CardStatistics> {
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| {
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)?,
@@ -13,7 +16,8 @@ pub fn load_stats_of_set(set: &CardSetSettings, connection: &Connection) -> Vec<
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<CardStatistics>, 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::<Vec<_>>().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::<Vec<_>>()
.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<CardStatistics>, 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
),
(&stat.score, &stat.last_open.timestamp(), &stat.id),
)
.unwrap_or_else(|e| {println!("{}", e); 0});
.unwrap_or_else(|e| {
println!("{}", e);
0
});
println!("Updated stat: {}", time.elapsed().as_millis());
}
pub fn delete_stat(stat: &CardStatistics, connection: &Connection) {
+13 -5
View File
@@ -33,7 +33,6 @@ fn parse_history_items(strings: Vec<String>) -> Vec<HistoryItem> {
let mut items = Vec::with_capacity(strings.len());
for string in strings {
if let [time, word, mode, before, after] = string.split(';').collect::<Vec<&str>>()[..] {
items.push(HistoryItem {
timestamp: DateTime::from_timestamp(time.parse().unwrap(), 0).unwrap(),
word_id: word.parse::<u32>().unwrap(),
@@ -56,15 +55,24 @@ 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 {
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
WordOpenMode::None => 1,
},
item.before,
item.after);
item.after
);
writeln!(&mut file, "{}", line_str.to_string()).unwrap();
}
+3 -4
View File
@@ -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;
+9 -11
View File
@@ -1,10 +1,10 @@
use rusqlite::Connection;
pub fn get_setting(key: String, connection: &Connection) -> Option<String> {
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<String> {
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()
}
+2 -4
View File
@@ -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();
+4 -2
View File
@@ -27,9 +27,11 @@ pub async fn get_voice(text: &str) -> BufReader<File> {
.header("Content-Type", "application/json")
.body(query)
.send()
.await.unwrap()
.await
.unwrap()
.bytes()
.await.unwrap();
.await
.unwrap();
tokio::fs::write(&path, &audio).await.unwrap();
}
-1
View File
@@ -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(
+7 -19
View File
@@ -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;
+13 -17
View File
@@ -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<DictionaryQuizMessage> for DictionaryQuizState {
}
impl DictionaryQuizState {
pub fn new(
words: Vec<WordData>,
reverse: bool,
no_typing: bool,
) -> DictionaryQuizState {
pub fn new(words: Vec<WordData>, 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()
}
+6 -13
View File
@@ -51,7 +51,8 @@ impl HistoryState {
}
pub fn view(&self) -> Element<'_, HistoryMessage> {
back_overlay(iced::widget::row![
back_overlay(
iced::widget::row![
horizontal().width(FillPortion(1)),
scrollable(self.history_lines().padding(DEFAULT_SPACING))
.height(Fill)
@@ -59,18 +60,10 @@ impl HistoryState {
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()
.width(Fill)
.into(),
Back,
)
}
fn history_lines(&self) -> Column<'_, HistoryMessage> {
+14 -8
View File
@@ -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;
@@ -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::<Vec<_>>();
let mut sorted = set
.set
.clone()
.into_iter()
.zip(0..set.set.len())
.collect::<Vec<_>>();
sorted.sort_by_key(|c| c.0.score);
let mut worst = sorted[0..self.pool_size].iter().map(|(_, index)| *index).collect::<Vec<usize>>();
let mut worst = sorted[0..self.pool_size]
.iter()
.map(|(_, index)| *index)
.collect::<Vec<usize>>();
worst.shuffle(&mut rand::rng());
self.pool = worst;
}
+5 -12
View File
@@ -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<RootMessage> {
keyboard::listen().map(|e| Keyboard(e))
}
pub struct AppState {
pub dictionary: Vec<WordData>,
pub card_sets: Vec<CardSetSettings>,
@@ -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 }
}
+8 -12
View File
@@ -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<Page>,
}
+8 -6
View File
@@ -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 {
+18 -20
View File
@@ -1,41 +1,40 @@
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 {
#[derive(Clone, Debug)]
pub struct RandomizerState {
text: text_editor::Content,
list: Vec<String>,
}
}
#[derive(Debug, Clone)]
pub enum RandomizerMessage {
#[derive(Debug, Clone)]
pub enum RandomizerMessage {
Back,
Start,
Edit(text_editor::Action),
}
}
impl NavigatedPage<RandomizerMessage> for RandomizerState {
impl NavigatedPage<RandomizerMessage> for RandomizerState {
fn navigate(&self, message: &RandomizerMessage) -> Option<Page> {
if let Back = message {
return Some(Page::PreviousPage);
}
None
}
}
}
impl Default for RandomizerState {
impl Default for RandomizerState {
fn default() -> Self {
Self::new()
}
}
}
impl RandomizerState {
impl RandomizerState {
pub fn new() -> RandomizerState {
RandomizerState {
text: Default::default(),
@@ -78,5 +77,4 @@ pub mod randomizer {
Back,
)
}
}
}
+7 -4
View File
@@ -113,7 +113,8 @@ impl RepetitionState {
}
pub fn view(&self) -> Element<'_, RepetitionMessage> {
back_overlay(column![
back_overlay(
column![
container(self.draw_forward())
.width(Fill)
.height(Fill)
@@ -132,12 +133,14 @@ impl RepetitionState {
text!(
"Затронуто слов {}, {}%",
self.opened.len(),
(self.opened.len() as f32 / self.set.len() as f32 * 10000.0).round()
/ 100.0
(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> {
+61 -26
View File
@@ -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};
@@ -111,19 +113,25 @@ 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![
back_overlay(
row![
column![
scrollable(self.sets_list()).height(Fill),
button("Добавить").style(jl_button)
button("Добавить")
.style(jl_button)
.width(Fill)
.on_press(RepetitionsMessage::CreateSet),
]
@@ -135,12 +143,18 @@ impl RepetitionsState {
.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<usize>,
pub worst_words_list: Option<Vec<WordData>>,
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());
}
}
+26 -14
View File
@@ -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<Mutex<AppState>>
state: Arc<Mutex<AppState>>,
}
#[derive(Debug, Clone)]
@@ -61,7 +61,7 @@ impl NavigatedPage<SelectorMessage> for SelectorState {
impl SelectorState {
pub fn new(state: Arc<Mutex<AppState>>) -> 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),
)
+26 -22
View File
@@ -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)]
back_overlay(
row![space().width(Fill), self.sync_column(), space().width(Fill)]
.spacing(DEFAULT_SPACING)
.width(Fill).into(), Back)
.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,8 +191,12 @@ 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),
]
}
}
@@ -202,7 +206,7 @@ impl SyncState {
.into()
}
/* fn app_updater(&self) -> Element<'_, SyncMessage> {
/* fn app_updater(&self) -> Element<'_, SyncMessage> {
column![].width(Fill).into()
}*/
+10 -9
View File
@@ -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 {
@@ -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!("{} ", &current.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)
]