Compressing word fields
This commit is contained in:
1 parent
e5622c70de
commit
0de809fcce
4 files changed
+120
-80
No files matched your search
@@ -1,9 +1,9 @@
|
|||||||
use litemap::LiteMap;
|
|
||||||
use crate::data::entities::WordData;
|
use crate::data::entities::WordData;
|
||||||
use crate::data::repositories::{DataRepository, DataRepositoryInner};
|
use crate::data::repositories::{DataRepository, DataRepositoryInner};
|
||||||
|
use crate::data::Id;
|
||||||
use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet};
|
use crate::data_provider::{SqliteDbLayer, TableAccessQuerySet};
|
||||||
|
use litemap::LiteMap;
|
||||||
use rusqlite::{Error, Params, Row};
|
use rusqlite::{Error, Params, Row};
|
||||||
use crate::data::{Id, SmallId};
|
|
||||||
|
|
||||||
pub type WordRepository = DataRepository<WordData, WordDataLayer>;
|
pub type WordRepository = DataRepository<WordData, WordDataLayer>;
|
||||||
pub type WordRepositoryInner = DataRepositoryInner<WordData, WordDataLayer>;
|
pub type WordRepositoryInner = DataRepositoryInner<WordData, WordDataLayer>;
|
||||||
@@ -61,7 +61,7 @@ impl TableAccessQuerySet<WordData> for WordQuerySet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn additional_to_string(map: &LiteMap<Id, String>) -> String {
|
fn additional_to_string(map: &LiteMap<Id, String>) -> String {
|
||||||
let mut parts = vec![];
|
let mut parts = Vec::with_capacity(map.len() * 2);
|
||||||
for (id, value) in map {
|
for (id, value) in map {
|
||||||
parts.push(id.to_string());
|
parts.push(id.to_string());
|
||||||
parts.push(value.clone());
|
parts.push(value.clone());
|
||||||
@@ -69,7 +69,20 @@ fn additional_to_string(map: &LiteMap<Id, String>) -> String {
|
|||||||
parts.join("")
|
parts.join("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn additional_from_string_temp(data: String) -> LiteMap<Id, String> {
|
||||||
|
|
||||||
|
let pre_map : LiteMap<String, String> = serde_json::from_str(data.as_str()).unwrap();
|
||||||
|
LiteMap::from(pre_map.iter().map(|(k, v)| (match k.as_str() {
|
||||||
|
"reading" => Id(1),
|
||||||
|
"description" => Id(2),
|
||||||
|
"context" => Id(3),
|
||||||
|
&_ => Id(0) }, v.to_string())).collect())
|
||||||
|
}
|
||||||
|
|
||||||
fn additional_from_string(data: String) -> LiteMap<Id, String> {
|
fn additional_from_string(data: String) -> LiteMap<Id, String> {
|
||||||
|
if data.is_empty() {
|
||||||
|
return LiteMap::new()
|
||||||
|
}
|
||||||
let parts = data.split('').collect::<Vec<&str>>();
|
let parts = data.split('').collect::<Vec<&str>>();
|
||||||
let chunks = parts.chunks(2).collect::<Vec<&[&str]>>();
|
let chunks = parts.chunks(2).collect::<Vec<&[&str]>>();
|
||||||
let mut map = LiteMap::with_capacity(parts.len());
|
let mut map = LiteMap::with_capacity(parts.len());
|
||||||
|
|||||||
+42
-4
@@ -1,16 +1,17 @@
|
|||||||
use crate::app::AppState;
|
use crate::app::AppState;
|
||||||
use crate::data::entities::WordData;
|
use crate::data::Id;
|
||||||
use crate::data::entities::WordGroup;
|
use crate::data::entities::WordGroup;
|
||||||
|
use crate::data::entities::{CustomField, WordData};
|
||||||
use crate::data::repositories::{BatchedRepository, Repository};
|
use crate::data::repositories::{BatchedRepository, Repository};
|
||||||
use crate::data_provider::import::{ImportData, ImportGroup, get_words_of_group, load_groups};
|
use crate::data_provider::import::{ImportData, ImportGroup, get_words_of_group, load_groups};
|
||||||
use crate::fs::*;
|
use crate::fs::*;
|
||||||
use crate::jl_button;
|
|
||||||
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
||||||
use crate::notification::NotificationData::OpenFileError;
|
use crate::notification::NotificationData::OpenFileError;
|
||||||
use crate::notification::{MaybeError, NotificationData};
|
use crate::notification::{MaybeError, NotificationData};
|
||||||
use crate::pages::import::ImportMessage::*;
|
use crate::pages::import::ImportMessage::*;
|
||||||
use crate::push_error;
|
use crate::push_error;
|
||||||
use crate::styling::*;
|
use crate::styling::*;
|
||||||
|
use crate::{jl_button, push_error_task};
|
||||||
use iced::widget::button::danger;
|
use iced::widget::button::danger;
|
||||||
use iced::widget::container::success;
|
use iced::widget::container::success;
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
@@ -25,6 +26,7 @@ use log::info;
|
|||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rfd::AsyncFileDialog;
|
use rfd::AsyncFileDialog;
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -428,7 +430,19 @@ impl ImportState {
|
|||||||
let separator = self.separator.clone();
|
let separator = self.separator.clone();
|
||||||
let skip_empty = self.skip_empty;
|
let skip_empty = self.skip_empty;
|
||||||
let group = self.import_data.as_ref().unwrap().0[self.selected_index].clone();
|
let group = self.import_data.as_ref().unwrap().0[self.selected_index].clone();
|
||||||
|
let map_indices = Self::get_mapping_indices(&group);
|
||||||
let progress = self.progress.clone().unwrap();
|
let progress = self.progress.clone().unwrap();
|
||||||
|
if let Err(err) = Self::ensure_fields(&state, &map_indices) {
|
||||||
|
return push_error_task!(err);
|
||||||
|
}
|
||||||
|
let fields: HashMap<String, Id> = HashMap::from_iter(
|
||||||
|
state
|
||||||
|
.custom_fields
|
||||||
|
.read()
|
||||||
|
.iter()
|
||||||
|
.map(|f| (f.identifier.clone(), f.id)),
|
||||||
|
);
|
||||||
|
|
||||||
info!("{}", group.name);
|
info!("{}", group.name);
|
||||||
Task::perform(
|
Task::perform(
|
||||||
async move {
|
async move {
|
||||||
@@ -439,7 +453,6 @@ impl ImportState {
|
|||||||
let import_list = get_words_of_group(&connection, group.id)?;
|
let import_list = get_words_of_group(&connection, group.id)?;
|
||||||
let mut words_list = Vec::with_capacity(import_list.len());
|
let mut words_list = Vec::with_capacity(import_list.len());
|
||||||
|
|
||||||
let map_indices = Self::get_mapping_indices(&group);
|
|
||||||
let group_handle;
|
let group_handle;
|
||||||
{
|
{
|
||||||
let group_entity = WordGroup {
|
let group_entity = WordGroup {
|
||||||
@@ -474,7 +487,7 @@ impl ImportState {
|
|||||||
word.tags = collected_string;
|
word.tags = collected_string;
|
||||||
}
|
}
|
||||||
&_ => {
|
&_ => {
|
||||||
word.additional.insert((*dest).clone(), collected_string);
|
word.additional.insert(fields[dest], collected_string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -554,4 +567,29 @@ impl ImportState {
|
|||||||
|_| RootMessage::Import(NextProgress),
|
|_| RootMessage::Import(NextProgress),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ensure_fields(
|
||||||
|
state: &AppState,
|
||||||
|
import_group: &Vec<(String, Vec<usize>)>,
|
||||||
|
) -> Result<(), NotificationData> {
|
||||||
|
let mut fields_repo = state.custom_fields.write();
|
||||||
|
let mut current_fields: Vec<String> = fields_repo
|
||||||
|
.iter()
|
||||||
|
.map(|field| field.identifier.clone())
|
||||||
|
.collect();
|
||||||
|
current_fields.append(&mut vec!["key".to_string(), "value".to_string(), "tags".to_string()]);
|
||||||
|
for (field, _) in import_group {
|
||||||
|
if current_fields.contains(field) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fields_repo.create_now(CustomField {
|
||||||
|
id: Default::default(),
|
||||||
|
name: field.clone(),
|
||||||
|
identifier: field.clone(),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -262,19 +262,19 @@ impl RepetitionState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw_reading(&self, word: &WordData) -> Element<'_, RepetitionMessage> {
|
fn draw_reading(&self, word: &WordData) -> Element<'_, RepetitionMessage> {
|
||||||
match word.additional.get("reading") {
|
match word.additional.get(&Id(1)) {
|
||||||
None => space().into(),
|
None => space().into(),
|
||||||
Some(reading) => text!("{}", reading).size(24).into(),
|
Some(reading) => text!("{}", reading).size(24).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn draw_context(&self, word: &WordData) -> Element<'_, RepetitionMessage> {
|
fn draw_context(&self, word: &WordData) -> Element<'_, RepetitionMessage> {
|
||||||
match word.additional.get("context") {
|
match word.additional.get(&Id(3)) {
|
||||||
None => space().into(),
|
None => space().into(),
|
||||||
Some(context) => text!("{}", context).size(24).into(),
|
Some(context) => text!("{}", context).size(24).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn draw_description(&self, word: &WordData) -> Element<'_, RepetitionMessage> {
|
fn draw_description(&self, word: &WordData) -> Element<'_, RepetitionMessage> {
|
||||||
match word.additional.get("description") {
|
match word.additional.get(&Id(2)) {
|
||||||
None => space().into(),
|
None => space().into(),
|
||||||
Some(context) => text!("{}", context).size(24).into(),
|
Some(context) => text!("{}", context).size(24).into(),
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-70
@@ -1,5 +1,6 @@
|
|||||||
use crate::app::AppState;
|
use crate::app::AppState;
|
||||||
use crate::data::entities::{EntityHandle, WordData};
|
use crate::data::Id;
|
||||||
|
use crate::data::entities::{CustomField, EntityHandle, WordData};
|
||||||
use crate::data::repositories::Repository;
|
use crate::data::repositories::Repository;
|
||||||
use crate::pages::word::WordMessage::*;
|
use crate::pages::word::WordMessage::*;
|
||||||
use crate::styling::*;
|
use crate::styling::*;
|
||||||
@@ -7,15 +8,17 @@ use iced::widget::button::danger;
|
|||||||
use iced::widget::space::horizontal;
|
use iced::widget::space::horizontal;
|
||||||
use iced::widget::{button, column, row, rule, scrollable, text, text_input};
|
use iced::widget::{button, column, row, rule, scrollable, text, text_input};
|
||||||
use iced::{Element, Fill};
|
use iced::{Element, Fill};
|
||||||
|
use iced_core::Padding;
|
||||||
|
|
||||||
pub struct WordEditor<Message> {
|
pub struct WordEditor<Message> {
|
||||||
state: AppState,
|
state: AppState,
|
||||||
handle: EntityHandle<WordData>,
|
handle: EntityHandle<WordData>,
|
||||||
on_delete: Option<Message>,
|
on_delete: Option<Message>,
|
||||||
on_edit: Option<Message>,
|
on_edit: Option<Message>,
|
||||||
|
fields: Vec<CustomField>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Message> WordEditor<Message> {
|
impl<Message> WordEditor<Message> {
|
||||||
pub(crate) fn update(&mut self, message: WordMessage) -> Option<Message> {
|
pub(crate) fn update(&mut self, message: WordMessage) -> Option<Message> {
|
||||||
let dictionary = &mut self.state.dictionary.write();
|
let dictionary = &mut self.state.dictionary.write();
|
||||||
let word = dictionary.get_mut(&mut self.handle).unwrap();
|
let word = dictionary.get_mut(&mut self.handle).unwrap();
|
||||||
@@ -37,7 +40,7 @@ impl<Message> WordEditor<Message> {
|
|||||||
word.additional.insert(key, "".to_string());
|
word.additional.insert(key, "".to_string());
|
||||||
}
|
}
|
||||||
RemoveAdditional(key) => {
|
RemoveAdditional(key) => {
|
||||||
word.additional.remove(key.as_str());
|
word.additional.remove(&key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.on_edit.take()
|
self.on_edit.take()
|
||||||
@@ -45,36 +48,25 @@ impl<Message> WordEditor<Message> {
|
|||||||
|
|
||||||
pub(crate) fn view(&self) -> Element<'_, WordMessage> {
|
pub(crate) fn view(&self) -> Element<'_, WordMessage> {
|
||||||
let mut fast_add = row![];
|
let mut fast_add = row![];
|
||||||
let word = self.state.dictionary.read().get_static(self.handle).unwrap();
|
let word = self
|
||||||
if !word.additional.contains_key("reading") {
|
.state
|
||||||
fast_add = fast_add.push(
|
.dictionary
|
||||||
button("Чтение")
|
.read()
|
||||||
.style(button::text)
|
.get_static(self.handle)
|
||||||
.on_press(AddAdditional("reading".to_string())),
|
.unwrap();
|
||||||
);
|
for field in &self.fields {
|
||||||
}
|
if !word.additional.contains_key(&field.id) {
|
||||||
if !word.additional.contains_key("description") {
|
fast_add = fast_add.push(
|
||||||
fast_add = fast_add.push(
|
button(field.name.as_str())
|
||||||
button("Описание")
|
.style(button::text)
|
||||||
.style(button::text)
|
.on_press(AddAdditional(field.id)),
|
||||||
.on_press(AddAdditional("description".to_string())),
|
);
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if !word.additional.contains_key("context") {
|
|
||||||
fast_add = fast_add.push(
|
|
||||||
button("В контексте")
|
|
||||||
.style(button::text)
|
|
||||||
.on_press(AddAdditional("context".to_string())),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut col = iced::widget::column![
|
let mut col = iced::widget::column![
|
||||||
column![
|
column![text!("Ключ"), text_input("key", &word.key).on_input(SetKey),]
|
||||||
text!("Ключ"),
|
.spacing(QUARTER_SPACING),
|
||||||
text_input("key", &word.key).on_input(SetKey),
|
|
||||||
]
|
|
||||||
.spacing(QUARTER_SPACING),
|
|
||||||
column![
|
column![
|
||||||
text!("Значение"),
|
text!("Значение"),
|
||||||
text_input("value", &word.value).on_input(SetValue),
|
text_input("value", &word.value).on_input(SetValue),
|
||||||
@@ -86,31 +78,44 @@ impl<Message> WordEditor<Message> {
|
|||||||
]
|
]
|
||||||
.spacing(QUARTER_SPACING),
|
.spacing(QUARTER_SPACING),
|
||||||
rule::horizontal(2),
|
rule::horizontal(2),
|
||||||
scrollable(fast_add),
|
scrollable(fast_add.padding(Padding{
|
||||||
|
|
||||||
|
|
||||||
|
top: 0.0 , right: 0.0 , bottom: 4.0 , left: 0.0 , })).horizontal(),
|
||||||
];
|
];
|
||||||
|
|
||||||
for more in &word.additional {
|
for more in &word.additional {
|
||||||
col = col.push(self.get_view_for_more(more));
|
col = col.push(self.get_view_for_more(more));
|
||||||
}
|
}
|
||||||
|
|
||||||
column![
|
column![
|
||||||
col.spacing(DEFAULT_SPACING / 2.0).width(Fill).height(Fill),
|
col.spacing(DEFAULT_SPACING / 2.0).width(Fill).height(Fill),
|
||||||
row![
|
row![
|
||||||
horizontal().width(Fill),
|
horizontal().width(Fill),
|
||||||
button("Удалить").style(danger).on_press(Delete),
|
button("Удалить").style(danger).on_press(Delete),
|
||||||
]
|
|
||||||
.spacing(DEFAULT_SPACING)
|
|
||||||
]
|
]
|
||||||
.spacing(DEFAULT_SPACING).into()
|
.spacing(DEFAULT_SPACING)
|
||||||
|
]
|
||||||
|
.spacing(DEFAULT_SPACING)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Message> WordEditor<Message> {
|
impl<Message> WordEditor<Message> {
|
||||||
pub fn new(handle: EntityHandle<WordData>, state: AppState) -> Self {
|
pub fn new(handle: EntityHandle<WordData>, state: AppState) -> Self {
|
||||||
Self { state, handle, on_delete: None, on_edit: None }
|
let fields = state
|
||||||
|
.custom_fields
|
||||||
|
.read()
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
Self {
|
||||||
|
state,
|
||||||
|
handle,
|
||||||
|
on_delete: None,
|
||||||
|
on_edit: None,
|
||||||
|
fields,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_delete(mut self, message: Message) -> Self {
|
pub fn on_delete(mut self, message: Message) -> Self {
|
||||||
@@ -124,38 +129,22 @@ impl<Message> WordEditor<Message> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Message> WordEditor<Message> {
|
impl<Message> WordEditor<Message> {
|
||||||
fn get_view_for_more(&self, value: (&String, &String)) -> Element<'_, WordMessage> {
|
fn get_view_for_more(&self, value: (&Id, &String)) -> Element<'_, WordMessage> {
|
||||||
match value.0.as_str() {
|
let field = self.fields.iter().find(|f| f.id == *value.0).unwrap();
|
||||||
"reading" => self.reading_field(value.1),
|
match value.0 {
|
||||||
"description" => self.description_field(value.1),
|
_ => self.additional_field(value.1, field.name.as_str(), value.0.clone()),
|
||||||
"context" => self.context_field(value.1),
|
|
||||||
_ => self.additional_field(value.1, value.0.clone(), value.0.clone()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reading_field(&self, value: &str) -> Element<'_, WordMessage> {
|
fn additional_field(&self, value: &str, name: &str, id: Id) -> Element<'_, WordMessage> {
|
||||||
self.additional_field(value, "Чтение слова".to_string(), "reading".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn description_field(&self, value: &str) -> Element<'_, WordMessage> {
|
|
||||||
self.additional_field(value, "Описание".to_string(), "description".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn context_field(&self, value: &str) -> Element<'_, WordMessage> {
|
|
||||||
self.additional_field(value, "В контексте".to_string(), "context".to_string())
|
|
||||||
}
|
|
||||||
fn additional_field(&self, value: &str, name: String, id: String) -> Element<'_, WordMessage> {
|
|
||||||
column![
|
column![
|
||||||
text!("{}", name),
|
text!("{}", name),
|
||||||
row![
|
row![
|
||||||
text_input(id.clone().as_str(), value)
|
text_input(name, value)
|
||||||
.on_input({
|
.on_input(move |string| SetAdditional(id, string))
|
||||||
let value = id.clone();
|
|
||||||
move |string| SetAdditional(value.clone(), string)
|
|
||||||
})
|
|
||||||
.width(Fill),
|
.width(Fill),
|
||||||
button("-")
|
button("-")
|
||||||
.on_press(RemoveAdditional(id.clone()))
|
.on_press(RemoveAdditional(id))
|
||||||
.style(danger),
|
.style(danger),
|
||||||
]
|
]
|
||||||
.spacing(QUARTER_SPACING),
|
.spacing(QUARTER_SPACING),
|
||||||
@@ -171,7 +160,7 @@ pub enum WordMessage {
|
|||||||
SetTags(String),
|
SetTags(String),
|
||||||
SetKey(String),
|
SetKey(String),
|
||||||
SetValue(String),
|
SetValue(String),
|
||||||
AddAdditional(String),
|
AddAdditional(Id),
|
||||||
RemoveAdditional(String),
|
RemoveAdditional(Id),
|
||||||
SetAdditional(String, String),
|
SetAdditional(Id, String),
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user