Splitting kana set
This commit is contained in:
1 parent
36e5693564
commit
c74a0eb311
25 files changed
+367
-332
No files matched your search
@@ -0,0 +1 @@
|
||||
pub mod kana_set;
|
||||
@@ -0,0 +1,201 @@
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct KanaSet {
|
||||
name: String,
|
||||
pub(crate) chars_type: KanaType,
|
||||
pub(crate) dictionary: Vec<Vec<(String, String)>>,
|
||||
pub(crate) include_map: [bool; 10],
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum KanaType {
|
||||
Hiragana,
|
||||
Katakana,
|
||||
}
|
||||
impl KanaSet {
|
||||
pub fn hiragana() -> Self {
|
||||
Self {
|
||||
name: "Хирагана".to_string(),
|
||||
chars_type: KanaType::Hiragana,
|
||||
dictionary: vec![
|
||||
vec![
|
||||
(String::from("ぁ"), String::from("a")),
|
||||
(String::from("ぃ"), String::from("i")),
|
||||
(String::from("ぅ"), String::from("u")),
|
||||
(String::from("ぇ"), String::from("e")),
|
||||
(String::from("ぉ"), String::from("o")),
|
||||
],
|
||||
vec![
|
||||
(String::from("さ"), String::from("sa")),
|
||||
(String::from("し"), String::from("shi")),
|
||||
(String::from("す"), String::from("su")),
|
||||
(String::from("せ"), String::from("se")),
|
||||
(String::from("そ"), String::from("so")),
|
||||
],
|
||||
vec![
|
||||
(String::from("か"), String::from("ka")),
|
||||
(String::from("き"), String::from("ki")),
|
||||
(String::from("く"), String::from("ku")),
|
||||
(String::from("け"), String::from("ke")),
|
||||
(String::from("こ"), String::from("ko")),
|
||||
],
|
||||
// Ряд «та» (たちつてと)
|
||||
vec![
|
||||
(String::from("た"), String::from("ta")),
|
||||
(String::from("ち"), String::from("chi")),
|
||||
(String::from("つ"), String::from("tsu")),
|
||||
(String::from("て"), String::from("te")),
|
||||
(String::from("と"), String::from("to")),
|
||||
],
|
||||
// Ряд «на» (なにぬねの)
|
||||
vec![
|
||||
(String::from("な"), String::from("na")),
|
||||
(String::from("に"), String::from("ni")),
|
||||
(String::from("ぬ"), String::from("nu")),
|
||||
(String::from("ね"), String::from("ne")),
|
||||
(String::from("の"), String::from("no")),
|
||||
],
|
||||
// Ряд «ха» (はひふへほ)
|
||||
vec![
|
||||
(String::from("は"), String::from("ha")),
|
||||
(String::from("ひ"), String::from("hi")),
|
||||
(String::from("ふ"), String::from("fu")),
|
||||
(String::from("へ"), String::from("he")),
|
||||
(String::from("ほ"), String::from("ho")),
|
||||
],
|
||||
// Ряд «ма» (まみむめも)
|
||||
vec![
|
||||
(String::from("ま"), String::from("ma")),
|
||||
(String::from("み"), String::from("mi")),
|
||||
(String::from("む"), String::from("mu")),
|
||||
(String::from("め"), String::from("me")),
|
||||
(String::from("も"), String::from("mo")),
|
||||
],
|
||||
// Ряд «я» (やゆよ) — только 3 символа
|
||||
vec![
|
||||
(String::from("や"), String::from("ya")),
|
||||
(String::from("ゆ"), String::from("yu")),
|
||||
(String::from("よ"), String::from("yo")),
|
||||
],
|
||||
// Ряд «ра» (らりるれろ)
|
||||
vec![
|
||||
(String::from("ら"), String::from("ra")),
|
||||
(String::from("り"), String::from("ri")),
|
||||
(String::from("る"), String::from("ru")),
|
||||
(String::from("れ"), String::from("re")),
|
||||
(String::from("ろ"), String::from("ro")),
|
||||
],
|
||||
vec![
|
||||
(String::from("わ"), String::from("wa")),
|
||||
(String::from("を"), String::from("wo")),
|
||||
(String::from("ん"), String::from("n")),
|
||||
],
|
||||
],
|
||||
include_map: [true; 10],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn katakana() -> Self {
|
||||
Self {
|
||||
name: "Катакана".to_string(),
|
||||
chars_type: KanaType::Katakana,
|
||||
dictionary: vec![
|
||||
vec![
|
||||
(String::from("ァ"), String::from("a")),
|
||||
(String::from("ィ"), String::from("i")),
|
||||
(String::from("ゥ"), String::from("u")),
|
||||
(String::from("ェ"), String::from("e")),
|
||||
(String::from("ォ"), String::from("o")),
|
||||
],
|
||||
vec![
|
||||
(String::from("サ"), String::from("sa")),
|
||||
(String::from("シ"), String::from("shi")),
|
||||
(String::from("ス"), String::from("su")),
|
||||
(String::from("セ"), String::from("se")),
|
||||
(String::from("ソ"), String::from("so")),
|
||||
],
|
||||
vec![
|
||||
(String::from("カ"), String::from("ka")),
|
||||
(String::from("キ"), String::from("ki")),
|
||||
(String::from("ク"), String::from("ku")),
|
||||
(String::from("ケ"), String::from("ke")),
|
||||
(String::from("コ"), String::from("ko")),
|
||||
],
|
||||
// Ряд «та» (タチツテト)
|
||||
vec![
|
||||
(String::from("タ"), String::from("ta")),
|
||||
(String::from("チ"), String::from("chi")),
|
||||
(String::from("ツ"), String::from("tsu")),
|
||||
(String::from("テ"), String::from("te")),
|
||||
(String::from("ト"), String::from("to")),
|
||||
],
|
||||
// Ряд «на» (ナニヌネノ)
|
||||
vec![
|
||||
(String::from("ナ"), String::from("na")),
|
||||
(String::from("ニ"), String::from("ni")),
|
||||
(String::from("ヌ"), String::from("nu")),
|
||||
(String::from("ネ"), String::from("ne")),
|
||||
(String::from("ノ"), String::from("no")),
|
||||
],
|
||||
// Ряд «ха» (ハヒフヘホ)
|
||||
vec![
|
||||
(String::from("ハ"), String::from("ha")),
|
||||
(String::from("ヒ"), String::from("hi")),
|
||||
(String::from("フ"), String::from("fu")),
|
||||
(String::from("ヘ"), String::from("he")),
|
||||
(String::from("ホ"), String::from("ho")),
|
||||
],
|
||||
// Ряд «ма» (マミムメモ)
|
||||
vec![
|
||||
(String::from("マ"), String::from("ma")),
|
||||
(String::from("ミ"), String::from("mi")),
|
||||
(String::from("ム"), String::from("mu")),
|
||||
(String::from("メ"), String::from("me")),
|
||||
(String::from("モ"), String::from("mo")),
|
||||
],
|
||||
// Ряд «я» (ヤユヨ) — только 3 символа
|
||||
vec![
|
||||
(String::from("ヤ"), String::from("ya")),
|
||||
(String::from("ユ"), String::from("yu")),
|
||||
(String::from("ヨ"), String::from("yo")),
|
||||
],
|
||||
// Ряд «ра» (ラリルレロ)
|
||||
vec![
|
||||
(String::from("ラ"), String::from("ra")),
|
||||
(String::from("リ"), String::from("ri")),
|
||||
(String::from("ル"), String::from("ru")),
|
||||
(String::from("レ"), String::from("re")),
|
||||
(String::from("ロ"), String::from("ro")),
|
||||
],
|
||||
vec![
|
||||
(String::from("ワ"), String::from("wa")),
|
||||
(String::from("ヲ"), String::from("wo")),
|
||||
(String::from("ン"), String::from("n")),
|
||||
],
|
||||
],
|
||||
include_map: [true; 10],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list(&self) -> Vec<(String, String)> {
|
||||
let mut current_set: Vec<(String, String)> = Vec::new();
|
||||
|
||||
for i in 0..10 {
|
||||
if self.include_map[i] {
|
||||
self.dictionary[i].iter().for_each(|v| {
|
||||
current_set.push(v.clone());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
current_set
|
||||
}
|
||||
}
|
||||
impl Default for KanaSet {
|
||||
fn default() -> Self {
|
||||
KanaSet::hiragana()
|
||||
}
|
||||
}
|
||||
impl PartialEq<Self> for KanaSet {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.name == other.name
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,21 @@
|
||||
use crate::dictionary::app_data_dir;
|
||||
use crate::fs::*;
|
||||
use crate::lang::{Id, WordOpenMode};
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::fs;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::PathBuf;
|
||||
use crate::notification::NotificationData;
|
||||
use crate::notification::NotificationData::PermissionDenied;
|
||||
|
||||
pub fn get_history_of_set(id: Id) -> Vec<HistoryItem> {
|
||||
let app_dir = history_dir();
|
||||
pub fn get_history_of_set(id: Id) -> Result<Vec<HistoryItem>, NotificationData> {
|
||||
let app_dir = history_dir()?;
|
||||
let head = app_dir.clone().join(format!("set_{}_history.csv", id));
|
||||
let mut lines = Vec::new();
|
||||
|
||||
append_lines_if_exists(head, &mut lines);
|
||||
lines.reverse();
|
||||
parse_history_items(lines)
|
||||
Ok(parse_history_items(lines))
|
||||
}
|
||||
|
||||
fn append_lines_if_exists(path: PathBuf, lines: &mut Vec<String>) {
|
||||
@@ -56,15 +57,15 @@ fn parse_history_items(strings: Vec<String>) -> Vec<HistoryItem> {
|
||||
items
|
||||
}
|
||||
|
||||
pub fn push_note(set_id: Id, item: HistoryItem) {
|
||||
let app_dir = history_dir();
|
||||
pub fn push_note(set_id: Id, item: HistoryItem) -> Result<(), NotificationData> {
|
||||
let app_dir = history_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();
|
||||
.map_err(|_| PermissionDenied)?;
|
||||
let line_str = format!(
|
||||
"{};{};{};{};{}",
|
||||
item.timestamp.timestamp(),
|
||||
@@ -78,15 +79,8 @@ pub fn push_note(set_id: Id, item: HistoryItem) {
|
||||
item.before,
|
||||
item.after
|
||||
);
|
||||
writeln!(&mut file, "{}", line_str).unwrap();
|
||||
}
|
||||
|
||||
pub fn history_dir() -> PathBuf {
|
||||
let directory = app_data_dir().join("history");
|
||||
if !directory.exists() {
|
||||
fs::create_dir(directory.clone()).unwrap();
|
||||
}
|
||||
directory
|
||||
writeln!(&mut file, "{}", line_str).map_err(|_| PermissionDenied)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use crate::dictionary::app_data_dir;
|
||||
use rusqlite::Connection;
|
||||
use crate::fs::*;
|
||||
|
||||
pub fn create_db() {
|
||||
let path = app_data_dir();
|
||||
let db_file = path.join("data.db");
|
||||
let db_file = db_path();
|
||||
if !db_file.exists() {
|
||||
std::fs::File::create(&db_file).unwrap();
|
||||
let connection = Connection::open(&db_file).unwrap();
|
||||
@@ -51,8 +50,7 @@ fn make_card_stats(conn: &Connection) -> Result<(), rusqlite::Error> {
|
||||
}
|
||||
|
||||
pub fn default_connection() -> Connection {
|
||||
let path = app_data_dir();
|
||||
let db_file = path.join("data.db");
|
||||
let db_file = db_path();
|
||||
let connection = Connection::open(db_file).unwrap();
|
||||
connection.execute("PRAGMA foreign_keys = ON;", []).unwrap();
|
||||
connection
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
use crate::dictionary::app_data_dir;
|
||||
use reqwest::Client;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use crate::fs::*;
|
||||
use crate::notification::NotificationData;
|
||||
use crate::notification::NotificationData::*;
|
||||
|
||||
pub async fn get_voice(text: &str) -> Result<BufReader<File>, NotificationData> {
|
||||
let mut path = app_data_dir();
|
||||
path.push("voice");
|
||||
if !path.exists() {
|
||||
std::fs::create_dir(path.clone()).map_err(|_| PermissionDenied)?;
|
||||
}
|
||||
let mut path = voice_dir()?;
|
||||
|
||||
let hash = format!("{}.wav", hex::encode(Sha256::digest(text.as_bytes())));
|
||||
path.push(hash);
|
||||
if !path.exists() {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::dictionary::app_data_dir;
|
||||
use std::io;
|
||||
use log::info;
|
||||
use tokio::fs::{File, OpenOptions};
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use zstd::{DEFAULT_COMPRESSION_LEVEL, Decoder, Encoder};
|
||||
use crate::fs::*;
|
||||
use crate::notification::NotificationData;
|
||||
use crate::notification::NotificationData::{PermissionDenied, WebApiUnavailable};
|
||||
|
||||
@@ -11,8 +11,7 @@ use crate::notification::NotificationData::{PermissionDenied, WebApiUnavailable}
|
||||
const API_URL: &str = "https://learning.micialware.ru/";
|
||||
|
||||
pub async fn send_data(id: String) -> Result<(), NotificationData> {
|
||||
let path = app_data_dir();
|
||||
let db_file = path.join("data.db");
|
||||
let db_file = db_path();
|
||||
let data = tokio::fs::read(db_file).await.map_err(|_| WebApiUnavailable)?;
|
||||
let data = compress(data);
|
||||
|
||||
@@ -102,7 +101,7 @@ pub async fn get_web_version(key: &str) -> Result<u32, NotificationData> {
|
||||
}
|
||||
|
||||
pub async fn get_local_version() -> u32 {
|
||||
let file = app_data_dir().join("data.meta");
|
||||
let file = db_meta_path();
|
||||
if file.exists() {
|
||||
let mut data = String::new();
|
||||
File::open(file.clone())
|
||||
@@ -126,7 +125,7 @@ pub async fn get_local_version() -> u32 {
|
||||
}
|
||||
|
||||
pub async fn set_local_version(version: u32) {
|
||||
let file_path = app_data_dir().join("data.meta");
|
||||
let file_path = db_meta_path();
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use crate::notification::NotificationData;
|
||||
use crate::notification::NotificationData::PermissionDenied;
|
||||
|
||||
pub fn app_data_dir() -> PathBuf {
|
||||
let mut dir = dirs::data_dir().unwrap();
|
||||
dir.push("jap_learn");
|
||||
if !dir.exists() {
|
||||
fs::create_dir_all(dir.clone()).unwrap();
|
||||
}
|
||||
|
||||
dir
|
||||
}
|
||||
|
||||
pub fn db_path() -> PathBuf {
|
||||
app_data_dir().join("data.db")
|
||||
}
|
||||
pub fn temp_db_path() -> PathBuf {
|
||||
app_data_dir().join("data.db.tmp")
|
||||
}
|
||||
|
||||
pub fn temp_import_path() -> PathBuf {
|
||||
app_data_dir().join("import")
|
||||
}
|
||||
|
||||
pub fn db_meta_path() -> PathBuf {
|
||||
app_data_dir().join("data.meta")
|
||||
}
|
||||
|
||||
pub fn history_dir() -> Result<PathBuf, NotificationData> {
|
||||
let directory = app_data_dir().join("history");
|
||||
if !directory.exists() {
|
||||
fs::create_dir_all(directory.clone()).map_err(|_| PermissionDenied)?;
|
||||
}
|
||||
Ok(directory)
|
||||
}
|
||||
|
||||
pub fn voice_dir() -> Result<PathBuf, NotificationData> {
|
||||
let directory = app_data_dir().join("voice");
|
||||
if !directory.exists() {
|
||||
fs::create_dir_all(directory.clone()).map_err(|_| PermissionDenied)?;
|
||||
}
|
||||
Ok(directory)
|
||||
}
|
||||
+2
-202
@@ -83,207 +83,7 @@ impl Id {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct KanaSet {
|
||||
name: String,
|
||||
pub(crate) chars_type: KanaType,
|
||||
pub(crate) dictionary: Vec<Vec<(String, String)>>,
|
||||
pub(crate) include_map: [bool; 10],
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum KanaType {
|
||||
Hiragana,
|
||||
Katakana,
|
||||
}
|
||||
impl KanaSet {
|
||||
pub fn hiragana() -> Self {
|
||||
Self {
|
||||
name: "Хирагана".to_string(),
|
||||
chars_type: KanaType::Hiragana,
|
||||
dictionary: vec![
|
||||
vec![
|
||||
(String::from("ぁ"), String::from("a")),
|
||||
(String::from("ぃ"), String::from("i")),
|
||||
(String::from("ぅ"), String::from("u")),
|
||||
(String::from("ぇ"), String::from("e")),
|
||||
(String::from("ぉ"), String::from("o")),
|
||||
],
|
||||
vec![
|
||||
(String::from("さ"), String::from("sa")),
|
||||
(String::from("し"), String::from("shi")),
|
||||
(String::from("す"), String::from("su")),
|
||||
(String::from("せ"), String::from("se")),
|
||||
(String::from("そ"), String::from("so")),
|
||||
],
|
||||
vec![
|
||||
(String::from("か"), String::from("ka")),
|
||||
(String::from("き"), String::from("ki")),
|
||||
(String::from("く"), String::from("ku")),
|
||||
(String::from("け"), String::from("ke")),
|
||||
(String::from("こ"), String::from("ko")),
|
||||
],
|
||||
// Ряд «та» (たちつてと)
|
||||
vec![
|
||||
(String::from("た"), String::from("ta")),
|
||||
(String::from("ち"), String::from("chi")),
|
||||
(String::from("つ"), String::from("tsu")),
|
||||
(String::from("て"), String::from("te")),
|
||||
(String::from("と"), String::from("to")),
|
||||
],
|
||||
// Ряд «на» (なにぬねの)
|
||||
vec![
|
||||
(String::from("な"), String::from("na")),
|
||||
(String::from("に"), String::from("ni")),
|
||||
(String::from("ぬ"), String::from("nu")),
|
||||
(String::from("ね"), String::from("ne")),
|
||||
(String::from("の"), String::from("no")),
|
||||
],
|
||||
// Ряд «ха» (はひふへほ)
|
||||
vec![
|
||||
(String::from("は"), String::from("ha")),
|
||||
(String::from("ひ"), String::from("hi")),
|
||||
(String::from("ふ"), String::from("fu")),
|
||||
(String::from("へ"), String::from("he")),
|
||||
(String::from("ほ"), String::from("ho")),
|
||||
],
|
||||
// Ряд «ма» (まみむめも)
|
||||
vec![
|
||||
(String::from("ま"), String::from("ma")),
|
||||
(String::from("み"), String::from("mi")),
|
||||
(String::from("む"), String::from("mu")),
|
||||
(String::from("め"), String::from("me")),
|
||||
(String::from("も"), String::from("mo")),
|
||||
],
|
||||
// Ряд «я» (やゆよ) — только 3 символа
|
||||
vec![
|
||||
(String::from("や"), String::from("ya")),
|
||||
(String::from("ゆ"), String::from("yu")),
|
||||
(String::from("よ"), String::from("yo")),
|
||||
],
|
||||
// Ряд «ра» (らりるれろ)
|
||||
vec![
|
||||
(String::from("ら"), String::from("ra")),
|
||||
(String::from("り"), String::from("ri")),
|
||||
(String::from("る"), String::from("ru")),
|
||||
(String::from("れ"), String::from("re")),
|
||||
(String::from("ろ"), String::from("ro")),
|
||||
],
|
||||
vec![
|
||||
(String::from("わ"), String::from("wa")),
|
||||
(String::from("を"), String::from("wo")),
|
||||
(String::from("ん"), String::from("n")),
|
||||
],
|
||||
],
|
||||
include_map: [true; 10],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn katakana() -> Self {
|
||||
Self {
|
||||
name: "Катакана".to_string(),
|
||||
chars_type: KanaType::Katakana,
|
||||
dictionary: vec![
|
||||
vec![
|
||||
(String::from("ァ"), String::from("a")),
|
||||
(String::from("ィ"), String::from("i")),
|
||||
(String::from("ゥ"), String::from("u")),
|
||||
(String::from("ェ"), String::from("e")),
|
||||
(String::from("ォ"), String::from("o")),
|
||||
],
|
||||
vec![
|
||||
(String::from("サ"), String::from("sa")),
|
||||
(String::from("シ"), String::from("shi")),
|
||||
(String::from("ス"), String::from("su")),
|
||||
(String::from("セ"), String::from("se")),
|
||||
(String::from("ソ"), String::from("so")),
|
||||
],
|
||||
vec![
|
||||
(String::from("カ"), String::from("ka")),
|
||||
(String::from("キ"), String::from("ki")),
|
||||
(String::from("ク"), String::from("ku")),
|
||||
(String::from("ケ"), String::from("ke")),
|
||||
(String::from("コ"), String::from("ko")),
|
||||
],
|
||||
// Ряд «та» (タチツテト)
|
||||
vec![
|
||||
(String::from("タ"), String::from("ta")),
|
||||
(String::from("チ"), String::from("chi")),
|
||||
(String::from("ツ"), String::from("tsu")),
|
||||
(String::from("テ"), String::from("te")),
|
||||
(String::from("ト"), String::from("to")),
|
||||
],
|
||||
// Ряд «на» (ナニヌネノ)
|
||||
vec![
|
||||
(String::from("ナ"), String::from("na")),
|
||||
(String::from("ニ"), String::from("ni")),
|
||||
(String::from("ヌ"), String::from("nu")),
|
||||
(String::from("ネ"), String::from("ne")),
|
||||
(String::from("ノ"), String::from("no")),
|
||||
],
|
||||
// Ряд «ха» (ハヒフヘホ)
|
||||
vec![
|
||||
(String::from("ハ"), String::from("ha")),
|
||||
(String::from("ヒ"), String::from("hi")),
|
||||
(String::from("フ"), String::from("fu")),
|
||||
(String::from("ヘ"), String::from("he")),
|
||||
(String::from("ホ"), String::from("ho")),
|
||||
],
|
||||
// Ряд «ма» (マミムメモ)
|
||||
vec![
|
||||
(String::from("マ"), String::from("ma")),
|
||||
(String::from("ミ"), String::from("mi")),
|
||||
(String::from("ム"), String::from("mu")),
|
||||
(String::from("メ"), String::from("me")),
|
||||
(String::from("モ"), String::from("mo")),
|
||||
],
|
||||
// Ряд «я» (ヤユヨ) — только 3 символа
|
||||
vec![
|
||||
(String::from("ヤ"), String::from("ya")),
|
||||
(String::from("ユ"), String::from("yu")),
|
||||
(String::from("ヨ"), String::from("yo")),
|
||||
],
|
||||
// Ряд «ра» (ラリルレロ)
|
||||
vec![
|
||||
(String::from("ラ"), String::from("ra")),
|
||||
(String::from("リ"), String::from("ri")),
|
||||
(String::from("ル"), String::from("ru")),
|
||||
(String::from("レ"), String::from("re")),
|
||||
(String::from("ロ"), String::from("ro")),
|
||||
],
|
||||
vec![
|
||||
(String::from("ワ"), String::from("wa")),
|
||||
(String::from("ヲ"), String::from("wo")),
|
||||
(String::from("ン"), String::from("n")),
|
||||
],
|
||||
],
|
||||
include_map: [true; 10],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list(&self) -> Vec<(String, String)> {
|
||||
let mut current_set: Vec<(String, String)> = Vec::new();
|
||||
|
||||
for i in 0..10 {
|
||||
if self.include_map[i] {
|
||||
self.dictionary[i].iter().for_each(|v| {
|
||||
current_set.push(v.clone());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
current_set
|
||||
}
|
||||
}
|
||||
impl Default for KanaSet {
|
||||
fn default() -> Self {
|
||||
KanaSet::hiragana()
|
||||
}
|
||||
}
|
||||
impl PartialEq<Self> for KanaSet {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.name == other.name
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WordData {
|
||||
pub id: Id,
|
||||
@@ -448,7 +248,7 @@ impl DeckData {
|
||||
}
|
||||
}
|
||||
update_stat_score(word, &self.state.lock().connection);
|
||||
push_note(
|
||||
let _ = push_note(
|
||||
self.settings.id,
|
||||
HistoryItem {
|
||||
timestamp: Utc::now(),
|
||||
@@ -457,7 +257,7 @@ impl DeckData {
|
||||
before: old_score,
|
||||
after: new_score,
|
||||
},
|
||||
)
|
||||
);
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
|
||||
+6
-18
@@ -1,26 +1,14 @@
|
||||
#![windows_subsystem = "windows"]
|
||||
mod data_provider;
|
||||
mod dictionary;
|
||||
mod dictionary_quiz;
|
||||
mod history;
|
||||
pub mod import;
|
||||
mod lang;
|
||||
pub mod navigation;
|
||||
mod quiz;
|
||||
mod randomizer;
|
||||
mod repetition;
|
||||
mod repetition_settings;
|
||||
mod repetitions;
|
||||
mod selector;
|
||||
pub mod styling;
|
||||
mod sync;
|
||||
mod word;
|
||||
mod writing;
|
||||
pub mod notification;
|
||||
pub mod app;
|
||||
|
||||
mod lang;
|
||||
mod styling;
|
||||
mod pages;
|
||||
mod navigation;
|
||||
mod fs;
|
||||
mod core;
|
||||
use crate::app::start;
|
||||
use crate::navigation::RootMessage;
|
||||
use mimalloc::MiMalloc;
|
||||
|
||||
#[global_allocator]
|
||||
|
||||
+41
-34
@@ -1,39 +1,46 @@
|
||||
use crate::app::{fill_state, AppState};
|
||||
use crate::data_provider::history::{get_history_of_set, history_dir};
|
||||
use crate::data_provider::history::get_history_of_set;
|
||||
use crate::data_provider::sqlite::{create_db, default_connection};
|
||||
use crate::data_provider::web_api::{get_local_version, get_web_version, load_data, set_local_version};
|
||||
use crate::dictionary::{app_data_dir, DictionaryMessage, DictionaryState};
|
||||
use crate::dictionary_quiz::{DictionaryQuizMessage, DictionaryQuizState};
|
||||
use crate::history::{HistoryMessage, HistoryState};
|
||||
use crate::import::{ImportMessage, ImportState};
|
||||
use crate::data_provider::web_api::{
|
||||
get_local_version, get_web_version, load_data, set_local_version,
|
||||
};
|
||||
use crate::fs::*;
|
||||
use crate::lang::Id;
|
||||
use crate::message_navigation;
|
||||
use crate::navigation::Page::*;
|
||||
use crate::navigation::RootMessage::{DataLoaded, Keyboard, Notification, PushNotification, UpdateData};
|
||||
use crate::notification::{NotificationData, NotificationManager, NotificationMessage, NotificationRequest};
|
||||
use crate::quiz::{QuizMessage, QuizState};
|
||||
use crate::randomizer::{RandomizerMessage, RandomizerState};
|
||||
use crate::repetition::{RepetitionMessage, RepetitionState};
|
||||
use crate::repetition_settings::{RepetitionSettingsMessage, RepetitionSettingsState};
|
||||
use crate::repetitions::{RepetitionsMessage, RepetitionsState};
|
||||
use crate::selector::{SelectorMessage, SelectorState};
|
||||
use crate::navigation::RootMessage::{
|
||||
DataLoaded, Keyboard, Notification, PushNotification, UpdateData,
|
||||
};
|
||||
use crate::notification::{
|
||||
NotificationData, NotificationManager, NotificationMessage, NotificationRequest,
|
||||
};
|
||||
use crate::pages::dictionary::{DictionaryMessage, DictionaryState};
|
||||
use crate::pages::dictionary_quiz::{DictionaryQuizMessage, DictionaryQuizState};
|
||||
use crate::pages::history::{HistoryMessage, HistoryState};
|
||||
use crate::pages::import::{ImportMessage, ImportState};
|
||||
use crate::pages::quiz::{QuizMessage, QuizState};
|
||||
use crate::pages::randomizer::{RandomizerMessage, RandomizerState};
|
||||
use crate::pages::repetition::{RepetitionMessage, RepetitionState};
|
||||
use crate::pages::repetition_settings::{RepetitionSettingsMessage, RepetitionSettingsState};
|
||||
use crate::pages::repetitions::{RepetitionsMessage, RepetitionsState};
|
||||
use crate::pages::selector::{SelectorMessage, SelectorState};
|
||||
use crate::pages::sync::{SyncMessage, SyncState};
|
||||
use crate::pages::word::{WordMessage, WordState};
|
||||
use crate::pages::writing::{WritingMessage, WritingState};
|
||||
use crate::state_navigate_handle;
|
||||
use crate::state_update;
|
||||
use crate::sync::{SyncMessage, SyncState};
|
||||
use crate::view_navigation;
|
||||
use crate::word::{WordMessage, WordState};
|
||||
use crate::writing::{WritingMessage, WritingState};
|
||||
use chrono::NaiveDate;
|
||||
use hashbrown::HashMap;
|
||||
use iced::keyboard::Event;
|
||||
use iced::widget::stack;
|
||||
use iced::{Element, Task};
|
||||
use iced_core::Length::Fill;
|
||||
use log::info;
|
||||
use parking_lot::Mutex;
|
||||
use rusqlite::Connection;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use log::info;
|
||||
|
||||
impl Default for ScreenState {
|
||||
fn default() -> Self {
|
||||
@@ -91,7 +98,7 @@ pub enum Page {
|
||||
pub struct ScreenState {
|
||||
stack: Vec<Page>,
|
||||
app_state: Arc<Mutex<AppState>>,
|
||||
notification_manager: NotificationManager
|
||||
notification_manager: NotificationManager,
|
||||
}
|
||||
|
||||
impl ScreenState {
|
||||
@@ -108,10 +115,7 @@ impl ScreenState {
|
||||
let key = state_guard.sync_data.key.clone().unwrap();
|
||||
final_task = Task::batch([
|
||||
reading_additional_task,
|
||||
Task::perform(
|
||||
Self::load_web_backup(key),
|
||||
|result| {
|
||||
match result {
|
||||
Task::perform(Self::load_web_backup(key), |result| match result {
|
||||
Ok(update) => {
|
||||
if update {
|
||||
UpdateData
|
||||
@@ -119,10 +123,8 @@ impl ScreenState {
|
||||
RootMessage::None
|
||||
}
|
||||
}
|
||||
Err(err) => PushNotification(NotificationRequest::Warning(err))
|
||||
}
|
||||
}
|
||||
),
|
||||
Err(err) => PushNotification(NotificationRequest::Warning(err)),
|
||||
}),
|
||||
]);
|
||||
} else {
|
||||
final_task = reading_additional_task;
|
||||
@@ -135,6 +137,7 @@ impl ScreenState {
|
||||
|
||||
async fn load_additional_data() -> RootMessage {
|
||||
let directory = history_dir();
|
||||
if let Ok(directory) = directory {
|
||||
let mut map = HashMap::new();
|
||||
for file in directory.read_dir().unwrap().flatten() {
|
||||
let mut vec = vec![];
|
||||
@@ -148,6 +151,7 @@ impl ScreenState {
|
||||
.unwrap();
|
||||
|
||||
let history = get_history_of_set(id);
|
||||
if let Ok(history) = history {
|
||||
let by_date = history.chunk_by(|x, x1| {
|
||||
x.timestamp.naive_local().date() == x1.timestamp.naive_local().date()
|
||||
});
|
||||
@@ -155,8 +159,13 @@ impl ScreenState {
|
||||
vec.push((group[0].timestamp.naive_local().date(), group.len() as u32));
|
||||
}
|
||||
map.insert(id, vec);
|
||||
} else {
|
||||
return PushNotification(NotificationRequest::Warning(history.err().unwrap()));
|
||||
}
|
||||
DataLoaded(map)
|
||||
}
|
||||
return DataLoaded(map);
|
||||
}
|
||||
PushNotification(NotificationRequest::Warning(directory.err().unwrap()))
|
||||
}
|
||||
|
||||
async fn load_web_backup(string: String) -> Result<bool, NotificationData> {
|
||||
@@ -191,9 +200,8 @@ impl ScreenState {
|
||||
if cfg!(windows) {
|
||||
state.connection = Connection::open_in_memory().unwrap();
|
||||
}
|
||||
let path = app_data_dir();
|
||||
let db_file = path.join("data.db");
|
||||
let temp_db_file = path.join("data.db.tmp");
|
||||
let db_file = db_path();
|
||||
let temp_db_file = temp_db_path();
|
||||
std::fs::rename(temp_db_file, &db_file).unwrap();
|
||||
|
||||
state.connection = default_connection();
|
||||
@@ -206,7 +214,7 @@ impl ScreenState {
|
||||
}
|
||||
|
||||
if let PushNotification(notification) = &message {
|
||||
return self.notification_manager.push(notification.clone())
|
||||
return self.notification_manager.push(notification.clone());
|
||||
}
|
||||
|
||||
let task: (Task<RootMessage>, bool) = self.navigate_or_update(message);
|
||||
@@ -255,7 +263,6 @@ impl ScreenState {
|
||||
}
|
||||
|
||||
pub fn view(&self) -> Element<'_, RootMessage> {
|
||||
|
||||
info!("View update");
|
||||
let time = Instant::now();
|
||||
let view = view_navigation!(
|
||||
@@ -275,7 +282,7 @@ impl ScreenState {
|
||||
Import
|
||||
);
|
||||
|
||||
let notifications = self.notification_manager.draw().map(RootMessage::Notification);
|
||||
let notifications = self.notification_manager.draw().map(Notification);
|
||||
|
||||
info!("View took {:?}", time.elapsed());
|
||||
stack![view, notifications].width(Fill).height(Fill).into()
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
pub mod dictionary;
|
||||
pub mod dictionary_quiz;
|
||||
pub mod history;
|
||||
pub mod import;
|
||||
pub mod quiz;
|
||||
pub mod randomizer;
|
||||
pub mod repetition;
|
||||
pub mod repetition_settings;
|
||||
pub mod repetitions;
|
||||
pub mod selector;
|
||||
pub mod sync;
|
||||
pub mod word;
|
||||
pub mod writing;
|
||||
@@ -1,12 +1,12 @@
|
||||
use crate::data_provider::words::{delete_group, delete_word, update_group, update_word};
|
||||
use crate::dictionary::DictionaryMessage::*;
|
||||
use crate::dictionary_quiz::DictionaryQuizState;
|
||||
use crate::import::ImportState;
|
||||
use crate::pages::dictionary::DictionaryMessage::*;
|
||||
use crate::pages::dictionary_quiz::DictionaryQuizState;
|
||||
use crate::pages::import::ImportState;
|
||||
use crate::lang::{WordData, WordGroup};
|
||||
use crate::navigation::Page::{Import, Word};
|
||||
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
||||
use crate::styling::*;
|
||||
use crate::word::WordState;
|
||||
use crate::pages::word::WordState;
|
||||
use chrono::{DateTime, TimeDelta, Utc};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use iced::alignment::Vertical::Center;
|
||||
@@ -619,15 +619,7 @@ pub fn split_with_coma(ts: &str) -> Vec<String> {
|
||||
.collect::<Vec<String>>()
|
||||
}
|
||||
|
||||
pub fn app_data_dir() -> PathBuf {
|
||||
let mut dir = dirs::data_dir().unwrap();
|
||||
dir.push("jap_learn");
|
||||
if !dir.exists() {
|
||||
fs::create_dir_all(dir.clone()).unwrap();
|
||||
}
|
||||
|
||||
dir
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
struct WordLineState {
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::RootMessage;
|
||||
use crate::dictionary::split_with_coma;
|
||||
use crate::dictionary_quiz::DictionaryQuizMessage::*;
|
||||
use crate::navigation::RootMessage;
|
||||
use crate::pages::dictionary::split_with_coma;
|
||||
use crate::pages::dictionary_quiz::DictionaryQuizMessage::*;
|
||||
use crate::lang::WordData;
|
||||
use crate::navigation::Page::PreviousPage;
|
||||
use crate::navigation::*;
|
||||
use crate::quiz::Score;
|
||||
use crate::pages::quiz::Score;
|
||||
use crate::styling::*;
|
||||
use iced::Background::Color;
|
||||
use iced::border::Radius;
|
||||
@@ -49,7 +49,7 @@ impl NavigatedPage<HistoryMessage> for HistoryState {
|
||||
impl HistoryState {
|
||||
pub fn new(id: crate::lang::Id, state: Arc<Mutex<AppState>>) -> Self {
|
||||
let state = state.lock();
|
||||
let history = get_history_of_set(id);
|
||||
let history = get_history_of_set(id).unwrap_or_default();
|
||||
let words = history
|
||||
.iter()
|
||||
.map(
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::data_provider::import::{ImportData, ImportGroup, get_words_of_group, load_groups};
|
||||
use crate::data_provider::words::{add_group, add_words};
|
||||
use crate::dictionary::app_data_dir;
|
||||
use crate::import::ImportMessage::*;
|
||||
use crate::pages::import::ImportMessage::*;
|
||||
use crate::lang::{WordData, WordGroup};
|
||||
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
||||
use crate::notification::NotificationData::OpenFileError;
|
||||
@@ -28,6 +27,7 @@ use log::info;
|
||||
use tokio::task::spawn_blocking;
|
||||
use zip::ZipArchive;
|
||||
use crate::app::AppState;
|
||||
use crate::fs::*;
|
||||
|
||||
const DEFAULT_FIELDS: [&str; 6] = ["key", "value", "tags", "reading", "context", "description"];
|
||||
|
||||
@@ -376,7 +376,7 @@ impl ImportState {
|
||||
let file = File::open(path).map_err(|_| OpenFileError)?;
|
||||
let reader = BufReader::new(file);
|
||||
let mut archive = ZipArchive::new(reader).map_err(|_| OpenFileError)?;
|
||||
let temp_file_path = app_data_dir().join("import");
|
||||
let temp_file_path = temp_import_path();
|
||||
let mut temp_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
@@ -394,7 +394,7 @@ impl ImportState {
|
||||
}
|
||||
}
|
||||
fn read_import_file() -> Result<ImportData, NotificationData> {
|
||||
let temp_file_path = app_data_dir().join("import");
|
||||
let temp_file_path = temp_import_path();
|
||||
let connection = Connection::open(&temp_file_path).map_err(|_| OpenFileError)?;
|
||||
let data = load_groups(&connection)?;
|
||||
Ok(data)
|
||||
@@ -431,7 +431,7 @@ impl ImportState {
|
||||
Task::perform(
|
||||
async move {
|
||||
spawn_blocking(move || {
|
||||
let temp_file_path = app_data_dir().join("import");
|
||||
let temp_file_path = temp_import_path();
|
||||
let connection = Connection::open(&temp_file_path).map_err(|_| OpenFileError)?;
|
||||
let import_list = get_words_of_group(&connection, group.id)?;
|
||||
let mut words_list = Vec::with_capacity(import_list.len());
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::lang::KanaSet;
|
||||
|
||||
use crate::navigation::Page::PreviousPage;
|
||||
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
||||
use crate::quiz::QuizMessage::*;
|
||||
use crate::pages::quiz::QuizMessage::*;
|
||||
use crate::styling::*;
|
||||
use iced::widget::*;
|
||||
use iced::{Element, Fill, Task, alignment};
|
||||
use rand::seq::SliceRandom;
|
||||
use crate::app::USER_FONT;
|
||||
use crate::core::kana_set::KanaSet;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct QuizState {
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::RootMessage;
|
||||
use crate::navigation::RootMessage;
|
||||
use crate::navigation::{NavigatedPage, Page};
|
||||
use crate::randomizer::RandomizerMessage::{Back, Edit, Start};
|
||||
use crate::pages::randomizer::RandomizerMessage::{Back, Edit, Start};
|
||||
use crate::styling::{HALF_SPACING, back_overlay, jl_button};
|
||||
use iced::Task;
|
||||
use iced::widget::{button, text_editor};
|
||||
File renamed without changes.
@@ -4,7 +4,7 @@ use crate::data_provider::card_stats::{delete_stats, load_stats_of_deck};
|
||||
use crate::lang::DeckSettings;
|
||||
use crate::navigation::Page::PreviousPage;
|
||||
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
||||
use crate::repetition_settings::RepetitionSettingsMessage::*;
|
||||
use crate::pages::repetition_settings::RepetitionSettingsMessage::*;
|
||||
use crate::styling::*;
|
||||
use iced::widget::button::danger;
|
||||
use iced::widget::text_input::Catalog;
|
||||
@@ -1,12 +1,12 @@
|
||||
use crate::data_provider::card_sets::update_deck;
|
||||
use crate::data_provider::card_stats::{add_stat_list, load_stats_of_deck};
|
||||
use crate::history::HistoryState;
|
||||
use crate::pages::history::HistoryState;
|
||||
use crate::lang::{AppendMode, CardStatistics, DeckSettings, Id, OrderMode};
|
||||
use crate::navigation::Page::{History, PreviousPage, Repetition, RepetitionSettings};
|
||||
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
||||
use crate::repetition::RepetitionState;
|
||||
use crate::repetition_settings::RepetitionSettingsState;
|
||||
use crate::repetitions::RepetitionsMessage::*;
|
||||
use crate::pages::repetition::RepetitionState;
|
||||
use crate::pages::repetition_settings::RepetitionSettingsState;
|
||||
use crate::pages::repetitions::RepetitionsMessage::*;
|
||||
use crate::styling::*;
|
||||
use chrono::{Days, Local, Utc};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
@@ -1,13 +1,12 @@
|
||||
use crate::dictionary::DictionaryState;
|
||||
use crate::lang::{KanaSet, KanaType};
|
||||
use crate::pages::dictionary::DictionaryState;
|
||||
use crate::navigation::Page::*;
|
||||
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
||||
use crate::randomizer::RandomizerState;
|
||||
use crate::repetitions::RepetitionsState;
|
||||
use crate::selector::SelectorMessage::ChangeMode;
|
||||
use crate::pages::randomizer::RandomizerState;
|
||||
use crate::pages::repetitions::RepetitionsState;
|
||||
use crate::pages::selector::SelectorMessage::ChangeMode;
|
||||
use crate::styling::*;
|
||||
use crate::sync::SyncState;
|
||||
use crate::writing::WritingState;
|
||||
use crate::pages::sync::SyncState;
|
||||
use crate::pages::writing::WritingState;
|
||||
use iced::widget::button::{Status, Style};
|
||||
use iced::widget::*;
|
||||
use iced::{Element, Task, alignment};
|
||||
@@ -17,7 +16,8 @@ use iced_core::Color;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
use crate::app::AppState;
|
||||
use crate::quiz::QuizState;
|
||||
use crate::core::kana_set::{KanaSet, KanaType};
|
||||
use crate::pages::quiz::QuizState;
|
||||
|
||||
pub struct SelectorState {
|
||||
pub set: KanaSet,
|
||||
@@ -3,7 +3,7 @@ use crate::data_provider::web_api::*;
|
||||
use crate::navigation::Page::PreviousPage;
|
||||
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
||||
use crate::styling::*;
|
||||
use crate::sync::SyncMessage::*;
|
||||
use crate::pages::sync::SyncMessage::*;
|
||||
use iced::widget::button::danger;
|
||||
use iced::widget::container::rounded_box;
|
||||
use iced::widget::{button, column, container, progress_bar, row, space, text, toggler};
|
||||
@@ -3,7 +3,7 @@ use crate::lang::WordData;
|
||||
use crate::navigation::Page::PreviousPage;
|
||||
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
||||
use crate::styling::*;
|
||||
use crate::word::WordMessage::*;
|
||||
use crate::pages::word::WordMessage::*;
|
||||
use iced::widget::button::danger;
|
||||
use iced::widget::{button, column, row, rule, scrollable, text, text_input};
|
||||
use iced::{Element, Fill, Task};
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::RootMessage;
|
||||
use crate::lang::KanaSet;
|
||||
|
||||
use crate::navigation::Page::PreviousPage;
|
||||
use crate::navigation::{NavigatedPage, Page};
|
||||
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
||||
use crate::styling::*;
|
||||
use iced::widget::*;
|
||||
use iced::{Element, Fill, Task, alignment};
|
||||
use rand::seq::SliceRandom;
|
||||
use crate::core::kana_set::KanaSet;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WritingState {
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::repetitions::Style;
|
||||
use iced::Element;
|
||||
use iced::border::Radius;
|
||||
use iced::widget::button::*;
|
||||
|
||||
Reference in new issue
Block a user