Repo integration #15
No files matched your search
+1
-1
@@ -5,7 +5,7 @@ create table states
|
|||||||
word_id INTEGER not null
|
word_id INTEGER not null
|
||||||
references words
|
references words
|
||||||
on delete cascade,
|
on delete cascade,
|
||||||
set_id INTEGER not null
|
deck_id INTEGER not null
|
||||||
references decks
|
references decks
|
||||||
on delete cascade,
|
on delete cascade,
|
||||||
score INTEGER default 1 not null,
|
score INTEGER default 1 not null,
|
||||||
|
|||||||
+9
-10
@@ -1,8 +1,11 @@
|
|||||||
use std::sync::Arc;
|
use crate::data::repositories::Repository;
|
||||||
use crate::data_provider::decks::{DeckRepository};
|
use crate::data_provider::card_states::CardStateRepository;
|
||||||
|
use crate::data_provider::decks::DeckRepository;
|
||||||
|
use crate::data_provider::history::HistoryRepository;
|
||||||
use crate::data_provider::settings::get_setting;
|
use crate::data_provider::settings::get_setting;
|
||||||
use crate::data_provider::sqlite::{SqliteConnection};
|
use crate::data_provider::sqlite::SqliteConnection;
|
||||||
use crate::data_provider::words::{WordRepository};
|
use crate::data_provider::word_groups::WordGroupRepository;
|
||||||
|
use crate::data_provider::words::WordRepository;
|
||||||
use crate::navigation::RootMessage::Keyboard;
|
use crate::navigation::RootMessage::Keyboard;
|
||||||
use crate::navigation::{AppSettings, RootMessage, ScreenState};
|
use crate::navigation::{AppSettings, RootMessage, ScreenState};
|
||||||
use iced::{Subscription, keyboard, window};
|
use iced::{Subscription, keyboard, window};
|
||||||
@@ -13,10 +16,7 @@ use iced_core::{Font, Size, Theme};
|
|||||||
use log::info;
|
use log::info;
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use crate::data::repositories::Repository;
|
use std::sync::Arc;
|
||||||
use crate::data_provider::card_states::CardStateRepository;
|
|
||||||
use crate::data_provider::history::HistoryRepository;
|
|
||||||
use crate::data_provider::word_groups::WordGroupRepository;
|
|
||||||
|
|
||||||
pub const USER_FONT: Font = Font::with_name("Noto Sans JP");
|
pub const USER_FONT: Font = Font::with_name("Noto Sans JP");
|
||||||
|
|
||||||
@@ -100,10 +100,9 @@ pub fn fill_state(state: &mut AppState) {
|
|||||||
let setting = load_settings(&connection);
|
let setting = load_settings(&connection);
|
||||||
|
|
||||||
state.word_groups.write().start().unwrap();
|
state.word_groups.write().start().unwrap();
|
||||||
state.dictionary.write().start().unwrap();
|
state.dictionary.write().start().unwrap();
|
||||||
state.decks.write().start().unwrap();
|
state.decks.write().start().unwrap();
|
||||||
|
|
||||||
|
|
||||||
state.sync_data = Arc::new(RwLock::new(setting))
|
state.sync_data = Arc::new(RwLock::new(setting))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -164,9 +164,9 @@ impl DeckData {
|
|||||||
self.set.len()
|
self.set.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list(deck: &DeckSettings, state: &AppState) -> Result<Vec<EntityHandle<WordData>>, NotificationData> {
|
pub fn list(deck: &DeckSettings, state: &AppState) -> Vec<EntityHandle<WordData>> {
|
||||||
let time = Instant::now();
|
let time = Instant::now();
|
||||||
let mut list = vec![];
|
let list = vec![];
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
let ast = engine.compile(&deck.filter);
|
let ast = engine.compile(&deck.filter);
|
||||||
@@ -201,9 +201,9 @@ impl DeckData {
|
|||||||
info!("Collecting available words is {:?}", time.elapsed());
|
info!("Collecting available words is {:?}", time.elapsed());
|
||||||
let handles =query.handles().collect();
|
let handles =query.handles().collect();
|
||||||
drop(query);
|
drop(query);
|
||||||
Ok(handles)
|
handles
|
||||||
} else {
|
} else {
|
||||||
Ok(list)
|
list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,7 +126,7 @@ impl DeckSettings {
|
|||||||
pub struct CardState {
|
pub struct CardState {
|
||||||
pub id: Id,
|
pub id: Id,
|
||||||
pub word_id: Id,
|
pub word_id: Id,
|
||||||
pub set_id: Id,
|
pub deck_id: Id,
|
||||||
pub last_open: DateTime<Utc>,
|
pub last_open: DateTime<Utc>,
|
||||||
pub score: u8,
|
pub score: u8,
|
||||||
pub fsrs_opened: NaiveDate,
|
pub fsrs_opened: NaiveDate,
|
||||||
@@ -200,6 +200,11 @@ impl<T> EntityHandle<T> {
|
|||||||
_phantom: Default::default(),
|
_phantom: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_id(id: Id) -> Self {
|
||||||
|
Self::new(id, id.0 as usize, false)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn index(&self) -> usize {
|
pub fn index(&self) -> usize {
|
||||||
(self.index << 1 >> 1) as usize
|
(self.index << 1 >> 1) as usize
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub type CardStateRepositoryInner = DataRepositoryInner<CardState, SqliteDbLayer
|
|||||||
pub struct CardStatesQuerySet;
|
pub struct CardStatesQuerySet;
|
||||||
impl TableAccessQuerySet<CardState> for CardStatesQuerySet {
|
impl TableAccessQuerySet<CardState> for CardStatesQuerySet {
|
||||||
fn load() -> &'static str {
|
fn load() -> &'static str {
|
||||||
"SELECT id, word_id, set_id, score, last_opened, fsrs_opened, fsrs_memory, fsrs_open_day FROM states"
|
"SELECT id, word_id, deck_id, score, last_opened, fsrs_opened, fsrs_memory, fsrs_open_day FROM states"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save() -> &'static str {
|
fn save() -> &'static str {
|
||||||
@@ -23,7 +23,7 @@ impl TableAccessQuerySet<CardState> for CardStatesQuerySet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add() -> &'static str {
|
fn add() -> &'static str {
|
||||||
"INSERT INTO states (word_id, set_id, score, last_opened, fsrs_opened, fsrs_memory, fsrs_open_day) VALUES (?1, ?2, ?3, ?4)"
|
"INSERT INTO states (word_id, deck_id, score, last_opened, fsrs_opened, fsrs_memory, fsrs_open_day) VALUES (?1, ?2, ?3, ?4)"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name() -> &'static str {
|
fn name() -> &'static str {
|
||||||
@@ -34,7 +34,7 @@ impl TableAccessQuerySet<CardState> for CardStatesQuerySet {
|
|||||||
Ok(CardState {
|
Ok(CardState {
|
||||||
id: row.get(0)?,
|
id: row.get(0)?,
|
||||||
word_id: row.get(1)?,
|
word_id: row.get(1)?,
|
||||||
set_id: row.get(2)?,
|
deck_id: row.get(2)?,
|
||||||
score: row.get(3)?,
|
score: row.get(3)?,
|
||||||
fsrs_opened: NaiveDate::from_epoch_days(row.get::<usize, i32>(5)?)
|
fsrs_opened: NaiveDate::from_epoch_days(row.get::<usize, i32>(5)?)
|
||||||
.expect("Incorrect date encoding"),
|
.expect("Incorrect date encoding"),
|
||||||
@@ -48,7 +48,7 @@ impl TableAccessQuerySet<CardState> for CardStatesQuerySet {
|
|||||||
fn insert_params(entity: &CardState) -> impl Params {
|
fn insert_params(entity: &CardState) -> impl Params {
|
||||||
(
|
(
|
||||||
entity.word_id,
|
entity.word_id,
|
||||||
entity.set_id,
|
entity.deck_id,
|
||||||
entity.score,
|
entity.score,
|
||||||
entity.last_open.timestamp(),
|
entity.last_open.timestamp(),
|
||||||
entity.fsrs_opened.to_epoch_days(),
|
entity.fsrs_opened.to_epoch_days(),
|
||||||
@@ -73,7 +73,7 @@ pub fn load_stats_of_deck(
|
|||||||
Ok(CardState {
|
Ok(CardState {
|
||||||
id: row.get(0)?,
|
id: row.get(0)?,
|
||||||
word_id: row.get(1)?,
|
word_id: row.get(1)?,
|
||||||
set_id: set.id,
|
deck_id: set.id,
|
||||||
score: row.get(2)?,
|
score: row.get(2)?,
|
||||||
fsrs_opened: NaiveDate::from_epoch_days(row.get::<usize, i32>(4)?)
|
fsrs_opened: NaiveDate::from_epoch_days(row.get::<usize, i32>(4)?)
|
||||||
.expect("Incorrect date encoding"),
|
.expect("Incorrect date encoding"),
|
||||||
@@ -126,7 +126,7 @@ pub fn add_stat_list(stats: &mut [CardState], connection: &mut Connection) -> Ma
|
|||||||
for stat in stats.iter() {
|
for stat in stats.iter() {
|
||||||
stmt.execute(params![
|
stmt.execute(params![
|
||||||
stat.word_id,
|
stat.word_id,
|
||||||
stat.set_id,
|
stat.deck_id,
|
||||||
stat.score,
|
stat.score,
|
||||||
stat.last_open.timestamp(),
|
stat.last_open.timestamp(),
|
||||||
stat.fsrs_opened.to_epoch_days(),
|
stat.fsrs_opened.to_epoch_days(),
|
||||||
|
|||||||
@@ -110,4 +110,7 @@ impl SqliteConnection {
|
|||||||
pub fn finish(&mut self) {
|
pub fn finish(&mut self) {
|
||||||
self.0 = None;
|
self.0 = None;
|
||||||
}
|
}
|
||||||
|
pub fn restart(&mut self) {
|
||||||
|
self.0 = Some(default_connection());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -86,12 +86,10 @@ impl NavigatedPage<RepetitionSettingsMessage> for RepetitionSettingsState {
|
|||||||
self.correct_filter = self.settings_data.check_filter();
|
self.correct_filter = self.settings_data.check_filter();
|
||||||
self.filter_changed = true;
|
self.filter_changed = true;
|
||||||
}
|
}
|
||||||
TryFilter => match DeckData::list(&self.settings_data, &self.state) {
|
TryFilter => {
|
||||||
Ok(list) => {
|
let list = DeckData::list(&self.settings_data, &self.state);
|
||||||
self.filtered_words = Some(list.len() as u32);
|
self.filtered_words = Some(list.len() as u32);
|
||||||
}
|
}
|
||||||
Err(err) => return push_error_task!(err),
|
|
||||||
},
|
|
||||||
DeleteSet => {
|
DeleteSet => {
|
||||||
if !self.real_delete {
|
if !self.real_delete {
|
||||||
self.real_delete = true;
|
self.real_delete = true;
|
||||||
@@ -183,10 +181,10 @@ fn update_deck_stats(deck: &DeckSettings, state: &AppState) -> MaybeError {
|
|||||||
let states_repo = &mut state.card_states.write();
|
let states_repo = &mut state.card_states.write();
|
||||||
let deck_id = deck.id;
|
let deck_id = deck.id;
|
||||||
let mut db_stats = states_repo
|
let mut db_stats = states_repo
|
||||||
.query_par(Arc::new(move |s| s.set_id == deck_id))
|
.query_par(Arc::new(move |s| s.deck_id == deck_id))
|
||||||
.iter(&states_repo)
|
.iter(&states_repo)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let filtered_words = DeckData::list(deck, state)?
|
let filtered_words = DeckData::list(deck, state)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|w| w.id)
|
.map(|w| w.id)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|||||||
+82
-95
@@ -1,8 +1,9 @@
|
|||||||
use crate::app::AppState;
|
use crate::app::AppState;
|
||||||
|
use crate::core::deck::DeckData;
|
||||||
use crate::core::srs::{AppendMode, OrderMode};
|
use crate::core::srs::{AppendMode, OrderMode};
|
||||||
use crate::data::Id;
|
use crate::data::entities::{CardState, DeckSettings, EntityHandle, WordData};
|
||||||
use crate::data::entities::{CardState, DeckSettings};
|
use crate::data::repositories::Repository;
|
||||||
use crate::data_provider::card_states::{add_stat_list, load_stats_of_deck};
|
use crate::data_provider::card_states::add_stat_list;
|
||||||
use crate::navigation::Page::{History, PreviousPage, Repetition, RepetitionSettings};
|
use crate::navigation::Page::{History, PreviousPage, Repetition, RepetitionSettings};
|
||||||
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
||||||
use crate::notification::{MaybeError, NotificationRequest};
|
use crate::notification::{MaybeError, NotificationRequest};
|
||||||
@@ -14,7 +15,7 @@ use crate::push_error;
|
|||||||
use crate::push_error_task;
|
use crate::push_error_task;
|
||||||
use crate::styling::*;
|
use crate::styling::*;
|
||||||
use chrono::{Days, Local, Utc};
|
use chrono::{Days, Local, Utc};
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::HashSet;
|
||||||
pub use iced::widget::button::{Catalog, Style};
|
pub use iced::widget::button::{Catalog, Style};
|
||||||
use iced::widget::button::{Status, danger};
|
use iced::widget::button::{Status, danger};
|
||||||
use iced::widget::tooltip::Position::Top;
|
use iced::widget::tooltip::Position::Top;
|
||||||
@@ -26,14 +27,12 @@ use iced::{Background, Border, Center, Color, Element, Fill, Length, Shadow, Tas
|
|||||||
use iced_core::Padding;
|
use iced_core::Padding;
|
||||||
use iced_core::border::Radius;
|
use iced_core::border::Radius;
|
||||||
use iced_core::svg::Handle;
|
use iced_core::svg::Handle;
|
||||||
use parking_lot::Mutex;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RepetitionsState {
|
pub struct RepetitionsState {
|
||||||
word_id_index_map: HashMap<Id, usize>,
|
selected_deck_handle: Option<EntityHandle<DeckSettings>>,
|
||||||
selected_deck_index: Option<usize>,
|
|
||||||
decks: Vec<DeckViewData>,
|
decks: Vec<DeckViewData>,
|
||||||
state: AppState,
|
state: AppState,
|
||||||
}
|
}
|
||||||
@@ -48,11 +47,10 @@ impl NavigatedPage<RepetitionsMessage> for RepetitionsState {
|
|||||||
self.state.clone(),
|
self.state.clone(),
|
||||||
)))
|
)))
|
||||||
} else if let GoToRepetition = message {
|
} else if let GoToRepetition = message {
|
||||||
let clone = self.state.clone();
|
let mut state_clone = self.state.clone();
|
||||||
|
|
||||||
let selected_set = self.selected_deck_mut().unwrap();
|
let selected_set = self.selected_deck_mut().unwrap();
|
||||||
if selected_set.append_mode == AppendMode::Full {
|
if selected_set.append_mode == AppendMode::Full {
|
||||||
if let Err(_err) = Self::append_all_words(&mut clone.lock(), selected_set) {
|
if let Err(_err) = Self::append_all_words(&mut state_clone, selected_set) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
selected_set.existing_words_indices = selected_set.available_words_indices.clone();
|
selected_set.existing_words_indices = selected_set.available_words_indices.clone();
|
||||||
@@ -65,7 +63,7 @@ impl NavigatedPage<RepetitionsMessage> for RepetitionsState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(repetition_state) =
|
if let Ok(repetition_state) =
|
||||||
RepetitionState::new(selected_set.general_settings.clone(), clone)
|
RepetitionState::new(selected_set.general_settings.clone(), self.state.clone())
|
||||||
{
|
{
|
||||||
return Some(Repetition(repetition_state));
|
return Some(Repetition(repetition_state));
|
||||||
}
|
}
|
||||||
@@ -73,7 +71,7 @@ impl NavigatedPage<RepetitionsMessage> for RepetitionsState {
|
|||||||
None
|
None
|
||||||
} else if let GoToSettings = message {
|
} else if let GoToSettings = message {
|
||||||
Some(RepetitionSettings(RepetitionSettingsState::new(
|
Some(RepetitionSettings(RepetitionSettingsState::new(
|
||||||
self.selected_deck().unwrap().index.unwrap(),
|
EntityHandle::from_id(self.selected_deck().unwrap().id),
|
||||||
self.state.clone(),
|
self.state.clone(),
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
@@ -81,23 +79,20 @@ impl NavigatedPage<RepetitionsMessage> for RepetitionsState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn navigated(&mut self) {
|
fn navigated(&mut self) {
|
||||||
if let Some(index) = self.selected_deck_index {
|
if let Some(index) = self.selected_deck_handle {
|
||||||
let state = self.state.lock();
|
let decks = self.state.decks.read();
|
||||||
if let Some(global_deck) = state.decks.get(index) {
|
if let None = decks.get_static(index) {
|
||||||
if global_deck.id != self.selected_deck().unwrap().id {
|
let position = self
|
||||||
drop(state);
|
.decks
|
||||||
self.decks.remove(index);
|
.iter()
|
||||||
self.clear_selection();
|
.position(|deck_view_data: &DeckViewData| deck_view_data.id == index.id)
|
||||||
} else {
|
.unwrap();
|
||||||
self.decks[index].general_settings = state.decks[index].clone();
|
self.decks.remove(position);
|
||||||
drop(state);
|
drop(decks);
|
||||||
let _ = self.fill_deck_word_indices();
|
self.clear_selection();
|
||||||
}
|
|
||||||
} else {
|
|
||||||
drop(state);
|
|
||||||
self.decks.remove(index);
|
|
||||||
self.clear_selection()
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let _ = self.fill_deck_word_indices();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn update(&mut self, message: RepetitionsMessage) -> Task<RootMessage> {
|
fn update(&mut self, message: RepetitionsMessage) -> Task<RootMessage> {
|
||||||
@@ -116,25 +111,27 @@ impl NavigatedPage<RepetitionsMessage> for RepetitionsState {
|
|||||||
available_words_indices: Default::default(),
|
available_words_indices: Default::default(),
|
||||||
indices_ready: false,
|
indices_ready: false,
|
||||||
append_mode: Default::default(),
|
append_mode: Default::default(),
|
||||||
index: None,
|
count: None,
|
||||||
|
open_mode: OrderMode::Default,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
DeleteSet => {
|
DeleteSet => {
|
||||||
let index = self.selected_deck_index.unwrap();
|
let index = self.selected_deck_handle.unwrap();
|
||||||
self.clear_selection();
|
self.clear_selection();
|
||||||
let deck = self.decks.remove(index);
|
let deck = self.decks.remove(
|
||||||
if deck.id.is_valid() {
|
self.decks
|
||||||
let mut state = self.state.lock();
|
.iter()
|
||||||
state.decks.remove(index);
|
.position(|dvd| dvd.id == index.id)
|
||||||
self.decks = self
|
.unwrap(),
|
||||||
.decks
|
);
|
||||||
.clone()
|
if index.real() {
|
||||||
.into_iter()
|
let mut repo = self.state.decks.write();
|
||||||
.map(|mut deck| {
|
if let Err(err) = repo.delete(index) {
|
||||||
deck.index = state.decks.iter().position(|d| d.id == deck.id);
|
return push_error_task!(err);
|
||||||
deck
|
}
|
||||||
})
|
|
||||||
.collect();
|
let index = self.decks.iter().position(|dvd| dvd.id == index.id);
|
||||||
|
self.decks.remove(index.unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SetName(value) => {
|
SetName(value) => {
|
||||||
@@ -152,20 +149,19 @@ impl NavigatedPage<RepetitionsMessage> for RepetitionsState {
|
|||||||
}
|
}
|
||||||
Save => {
|
Save => {
|
||||||
let mut deck = self.selected_deck().unwrap().clone();
|
let mut deck = self.selected_deck().unwrap().clone();
|
||||||
let mut state = self.state.lock();
|
let handle = self.selected_deck_handle.unwrap();
|
||||||
{
|
let mut repo = self.state.decks.write();
|
||||||
if let Err(e) = update_deck(&mut deck.general_settings, &state.connection) {
|
|
||||||
return Task::done(RootMessage::PushNotification(
|
repo.set_static(handle, *deck);
|
||||||
NotificationRequest::Error(e),
|
if let Err(err) = repo.save(&mut handle.clone()) {
|
||||||
));
|
return push_error_task!(err);
|
||||||
}
|
}
|
||||||
if let Some(index) = deck.index {
|
if let Some(index) = deck.index {
|
||||||
state.decks[index] = deck.general_settings.clone();
|
state.decks[index] = deck.general_settings.clone();
|
||||||
} else {
|
} else {
|
||||||
state.decks.push(deck.general_settings.clone());
|
state.decks.push(deck.general_settings.clone());
|
||||||
deck.index = Some(state.decks.len() - 1);
|
deck.index = Some(state.decks.len() - 1);
|
||||||
self.decks[self.selected_deck_index.unwrap()] = deck.clone();
|
self.decks[self.selected_deck_handle.unwrap()] = deck.clone();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SetForward(value) => {
|
SetForward(value) => {
|
||||||
@@ -181,23 +177,21 @@ impl NavigatedPage<RepetitionsMessage> for RepetitionsState {
|
|||||||
deck.general_settings.filter = value;
|
deck.general_settings.filter = value;
|
||||||
}
|
}
|
||||||
TryFilter => {
|
TryFilter => {
|
||||||
let state = self.state.lock();
|
|
||||||
let deck = self.selected_deck().unwrap();
|
let deck = self.selected_deck().unwrap();
|
||||||
let count = deck.get_word_list(&state).len();
|
let count = DeckData::list(&deck, &self.state).len();
|
||||||
drop(state);
|
|
||||||
let deck = self.selected_deck_mut().unwrap();
|
let deck = self.selected_deck_mut().unwrap();
|
||||||
deck.general_settings.count = Some(count);
|
deck.count = Some(count);
|
||||||
}
|
}
|
||||||
SetOpenMode(mode) => {
|
SetOpenMode(mode) => {
|
||||||
let deck = self.selected_deck_mut().unwrap();
|
let deck = self.selected_deck_mut().unwrap();
|
||||||
deck.general_settings.open_mode = mode;
|
deck.open_mode = mode;
|
||||||
}
|
}
|
||||||
SetAppendMode(mode) => {
|
SetAppendMode(mode) => {
|
||||||
let deck = self.selected_deck_mut().unwrap();
|
let deck = self.selected_deck_mut().unwrap();
|
||||||
deck.append_mode = mode;
|
deck.append_mode = mode;
|
||||||
}
|
}
|
||||||
AppendWords(count) => {
|
AppendWords(count) => {
|
||||||
let adding: Vec<usize>;
|
let adding: Vec<EntityHandle<WordData>>;
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
let deck = self.selected_deck().unwrap();
|
let deck = self.selected_deck().unwrap();
|
||||||
@@ -257,22 +251,9 @@ impl NavigatedPage<RepetitionsMessage> for RepetitionsState {
|
|||||||
|
|
||||||
impl RepetitionsState {
|
impl RepetitionsState {
|
||||||
pub(crate) fn new(state: AppState) -> RepetitionsState {
|
pub(crate) fn new(state: AppState) -> RepetitionsState {
|
||||||
let state_ = state.lock();
|
let decks = Self::get_decks_from_state(&state);
|
||||||
let mut map = HashMap::with_capacity(state_.dictionary.len());
|
|
||||||
|
|
||||||
state_
|
|
||||||
.dictionary
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.for_each(|(index, word)| {
|
|
||||||
map.insert(word.id, index);
|
|
||||||
});
|
|
||||||
|
|
||||||
let decks = Self::get_decks_from_state(&state_);
|
|
||||||
drop(state_);
|
|
||||||
RepetitionsState {
|
RepetitionsState {
|
||||||
word_id_index_map: map,
|
selected_deck_handle: None,
|
||||||
selected_deck_index: None,
|
|
||||||
decks,
|
decks,
|
||||||
state,
|
state,
|
||||||
}
|
}
|
||||||
@@ -281,6 +262,7 @@ impl RepetitionsState {
|
|||||||
fn get_decks_from_state(state: &AppState) -> Vec<DeckViewData> {
|
fn get_decks_from_state(state: &AppState) -> Vec<DeckViewData> {
|
||||||
state
|
state
|
||||||
.decks
|
.decks
|
||||||
|
.read()
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(index, deck)| DeckViewData {
|
.map(|(index, deck)| DeckViewData {
|
||||||
@@ -289,7 +271,9 @@ impl RepetitionsState {
|
|||||||
available_words_indices: Default::default(),
|
available_words_indices: Default::default(),
|
||||||
indices_ready: false,
|
indices_ready: false,
|
||||||
append_mode: Default::default(),
|
append_mode: Default::default(),
|
||||||
|
count: None,
|
||||||
index: Some(index),
|
index: Some(index),
|
||||||
|
open_mode: OrderMode::Default,
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@@ -526,7 +510,7 @@ impl RepetitionsState {
|
|||||||
text!("{} слова добавлено из {}", now, available).into()
|
text!("{} слова добавлено из {}", now, available).into()
|
||||||
}
|
}
|
||||||
fn count_view(&self, deck: &DeckSettings) -> Element<'_, RepetitionsMessage> {
|
fn count_view(&self, deck: &DeckSettings) -> Element<'_, RepetitionsMessage> {
|
||||||
if let Some(count) = deck.count {
|
if let Some(count) = self.decks.iter().find() {
|
||||||
return text!("Количество слов: {}", count).into();
|
return text!("Количество слов: {}", count).into();
|
||||||
}
|
}
|
||||||
space().into()
|
space().into()
|
||||||
@@ -578,7 +562,7 @@ impl RepetitionsState {
|
|||||||
fn append_words(
|
fn append_words(
|
||||||
state: &mut AppState,
|
state: &mut AppState,
|
||||||
deck: &DeckSettings,
|
deck: &DeckSettings,
|
||||||
indices: impl Iterator<Item = usize>,
|
indices: impl Iterator<Item = EntityHandle<WordData>>,
|
||||||
) -> MaybeError {
|
) -> MaybeError {
|
||||||
let words = &state.dictionary;
|
let words = &state.dictionary;
|
||||||
let stats = &mut indices
|
let stats = &mut indices
|
||||||
@@ -591,7 +575,7 @@ impl RepetitionsState {
|
|||||||
score: 1,
|
score: 1,
|
||||||
fsrs_opened: Utc::now().date_naive(),
|
fsrs_opened: Utc::now().date_naive(),
|
||||||
fsrs_memory: None,
|
fsrs_memory: None,
|
||||||
set_id: deck.id,
|
deck_id: deck.id,
|
||||||
fsrs_open_day: Utc::now().date_naive(),
|
fsrs_open_day: Utc::now().date_naive(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -605,32 +589,34 @@ impl RepetitionsState {
|
|||||||
|
|
||||||
fn select_deck(&mut self, index: usize) {
|
fn select_deck(&mut self, index: usize) {
|
||||||
if self.decks.get(index).is_some() {
|
if self.decks.get(index).is_some() {
|
||||||
self.selected_deck_index = Some(index);
|
self.selected_deck_handle = Some(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn selected_deck(&self) -> Option<&DeckViewData> {
|
fn selected_deck(&self) -> Option<&DeckViewData> {
|
||||||
self.decks.get(self.selected_deck_index?)
|
self.decks.get(self.selected_deck_handle?)
|
||||||
}
|
}
|
||||||
fn selected_deck_mut(&mut self) -> Option<&mut DeckViewData> {
|
fn selected_deck_mut(&mut self) -> Option<&mut DeckViewData> {
|
||||||
self.decks.get_mut(self.selected_deck_index?)
|
self.decks.get_mut(self.selected_deck_handle?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_selection(&mut self) {
|
fn clear_selection(&mut self) {
|
||||||
self.selected_deck_index = None;
|
self.selected_deck_handle = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_deck_word_indices(&mut self) -> MaybeError {
|
fn fill_deck_word_indices(&mut self) -> MaybeError {
|
||||||
let state = &self.state.lock();
|
let card_states = self.state.card_states.read();
|
||||||
|
let deck_id = self.selected_deck().unwrap().id;
|
||||||
|
let added = card_states
|
||||||
|
.query_par(Arc::new(move |state| state.deck_id == deck_id))
|
||||||
|
.iter(&card_states)
|
||||||
|
.map(|state| EntityHandle::from_id(state.word_id))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
let mut deck = self.selected_deck().unwrap().clone();
|
let mut deck = self.selected_deck().unwrap().clone();
|
||||||
let added: Vec<_> = load_stats_of_deck(&deck, &state.connection)?
|
let total = DeckData::list(&deck, &self.state)?;
|
||||||
.iter()
|
|
||||||
.map(|c| self.word_id_index_map[&c.word_id])
|
|
||||||
.collect();
|
|
||||||
let total = deck.get_word_list(state);
|
|
||||||
deck.existing_words_indices = added;
|
deck.existing_words_indices = added;
|
||||||
deck.available_words_indices = total;
|
deck.available_words_indices = total;
|
||||||
self.decks[self.selected_deck_index.unwrap()] = deck;
|
self.decks[self.selected_deck_handle.unwrap()] = deck;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -658,11 +644,12 @@ pub enum RepetitionsMessage {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct DeckViewData {
|
struct DeckViewData {
|
||||||
general_settings: DeckSettings,
|
general_settings: DeckSettings,
|
||||||
existing_words_indices: Vec<usize>,
|
existing_words_indices: Vec<EntityHandle<WordData>>,
|
||||||
available_words_indices: Vec<usize>,
|
available_words_indices: Vec<EntityHandle<WordData>>,
|
||||||
indices_ready: bool,
|
indices_ready: bool,
|
||||||
append_mode: AppendMode,
|
append_mode: AppendMode,
|
||||||
index: Option<usize>,
|
count: Option<usize>,
|
||||||
|
open_mode: OrderMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for DeckViewData {
|
impl Deref for DeckViewData {
|
||||||
|
|||||||
+47
-33
@@ -1,4 +1,5 @@
|
|||||||
use crate::app::{AppState, fill_state};
|
use crate::app::AppState;
|
||||||
|
use crate::data::repositories::Repository;
|
||||||
use crate::data_provider::settings::{delete_settings, set_setting};
|
use crate::data_provider::settings::{delete_settings, set_setting};
|
||||||
use crate::data_provider::web_api::*;
|
use crate::data_provider::web_api::*;
|
||||||
use crate::navigation::Page::PreviousPage;
|
use crate::navigation::Page::PreviousPage;
|
||||||
@@ -12,8 +13,6 @@ use iced::widget::container::rounded_box;
|
|||||||
use iced::widget::{button, column, container, progress_bar, row, space, text, toggler};
|
use iced::widget::{button, column, container, progress_bar, row, space, text, toggler};
|
||||||
use iced::{Center, Element, Fill, Font, Length, Task};
|
use iced::{Center, Element, Fill, Font, Length, Task};
|
||||||
use log::error;
|
use log::error;
|
||||||
use parking_lot::Mutex;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -56,8 +55,7 @@ impl NavigatedPage<SyncMessage> for SyncState {
|
|||||||
match message {
|
match message {
|
||||||
Back => {}
|
Back => {}
|
||||||
CopyKey => {
|
CopyKey => {
|
||||||
let state = self.state.lock();
|
let key = self.state.sync_data.read().key.clone().unwrap();
|
||||||
let key = state.sync_data.key.clone().unwrap();
|
|
||||||
return iced::clipboard::write(key)
|
return iced::clipboard::write(key)
|
||||||
.map(|_val: String| RootMessage::Sync(KeyCopied));
|
.map(|_val: String| RootMessage::Sync(KeyCopied));
|
||||||
}
|
}
|
||||||
@@ -76,16 +74,20 @@ impl NavigatedPage<SyncMessage> for SyncState {
|
|||||||
if !validate_id(&new_id) {
|
if !validate_id(&new_id) {
|
||||||
return Task::none();
|
return Task::none();
|
||||||
}
|
}
|
||||||
let mut state = self.state.lock();
|
let mut settings = self.state.sync_data.write();
|
||||||
state.sync_data.key = Some(new_id.clone());
|
settings.key = Some(new_id.clone());
|
||||||
if let Err(err) = set_setting("SYNC_KEY".to_string(), new_id, &state.connection) {
|
if let Err(err) = set_setting(
|
||||||
|
"SYNC_KEY".to_string(),
|
||||||
|
new_id,
|
||||||
|
&self.state.connection.lock(),
|
||||||
|
) {
|
||||||
return push_error_task!(err);
|
return push_error_task!(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Send => {
|
Send => {
|
||||||
self.prepare_to_db_interaction();
|
self.prepare_to_db_interaction();
|
||||||
|
|
||||||
let id = self.state.lock().sync_data.key.clone().unwrap();
|
let id = self.state.sync_data.read().key.clone().unwrap();
|
||||||
let tasks = Task::batch([
|
let tasks = Task::batch([
|
||||||
Task::perform(
|
Task::perform(
|
||||||
async { tokio::time::sleep(Duration::from_millis(200)).await },
|
async { tokio::time::sleep(Duration::from_millis(200)).await },
|
||||||
@@ -97,7 +99,7 @@ impl NavigatedPage<SyncMessage> for SyncState {
|
|||||||
return tasks;
|
return tasks;
|
||||||
}
|
}
|
||||||
GetLast => {
|
GetLast => {
|
||||||
let id = self.state.lock().sync_data.key.clone().unwrap();
|
let id = self.state.sync_data.read().key.clone().unwrap();
|
||||||
let tasks = Task::batch([
|
let tasks = Task::batch([
|
||||||
Task::perform(
|
Task::perform(
|
||||||
async { tokio::time::sleep(Duration::from_millis(200)).await },
|
async { tokio::time::sleep(Duration::from_millis(200)).await },
|
||||||
@@ -122,26 +124,27 @@ impl NavigatedPage<SyncMessage> for SyncState {
|
|||||||
}
|
}
|
||||||
NetworkFinished => {
|
NetworkFinished => {
|
||||||
self.frozen = false;
|
self.frozen = false;
|
||||||
let mut updated_state = AppState::new();
|
{
|
||||||
fill_state(&mut updated_state);
|
let mut connection = self.state.connection.lock();
|
||||||
|
connection.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = self.reload_data() {
|
||||||
|
return push_error_task!(err);
|
||||||
|
}
|
||||||
|
|
||||||
let mut state = self.state.lock();
|
|
||||||
state.sync_data = updated_state.sync_data;
|
|
||||||
state.connection = updated_state.connection;
|
|
||||||
state.decks = updated_state.decks;
|
|
||||||
state.dictionary = updated_state.dictionary;
|
|
||||||
state.word_groups = updated_state.word_groups;
|
|
||||||
return Task::done(RootMessage::PushNotification(NotificationRequest::Info(
|
return Task::done(RootMessage::PushNotification(NotificationRequest::Info(
|
||||||
NotificationData::DataLoaded,
|
NotificationData::DataLoaded,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
Disable => {}
|
Disable => {}
|
||||||
DisableSync => {
|
DisableSync => {
|
||||||
let mut state = self.state.lock();
|
let mut sync_data = self.state.sync_data.write();
|
||||||
state.sync_data.key = None;
|
let connection = self.state.connection.lock();
|
||||||
|
sync_data.key = None;
|
||||||
let save_result = || {
|
let save_result = || {
|
||||||
delete_settings("SYNC_KEY".to_string(), &state.connection)?;
|
delete_settings("SYNC_KEY".to_string(), &connection)?;
|
||||||
delete_settings("AUTO_WEB_FETCH".to_string(), &state.connection)?;
|
delete_settings("AUTO_WEB_FETCH".to_string(), &connection)?;
|
||||||
Ok::<(), NotificationData>(())
|
Ok::<(), NotificationData>(())
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -151,12 +154,14 @@ impl NavigatedPage<SyncMessage> for SyncState {
|
|||||||
}
|
}
|
||||||
SwitchAutoSync => {
|
SwitchAutoSync => {
|
||||||
self.auto_fetch = !self.auto_fetch;
|
self.auto_fetch = !self.auto_fetch;
|
||||||
let mut state = self.state.lock();
|
let mut sync_data = self.state.sync_data.write();
|
||||||
state.sync_data.auto_web_fetch = self.auto_fetch;
|
let connection = self.state.connection.lock();
|
||||||
|
|
||||||
|
sync_data.auto_web_fetch = self.auto_fetch;
|
||||||
if let Err(err) = set_setting(
|
if let Err(err) = set_setting(
|
||||||
"AUTO_WEB_FETCH".to_string(),
|
"AUTO_WEB_FETCH".to_string(),
|
||||||
self.auto_fetch.to_string(),
|
self.auto_fetch.to_string(),
|
||||||
&state.connection,
|
&connection,
|
||||||
) {
|
) {
|
||||||
return push_error_task!(err);
|
return push_error_task!(err);
|
||||||
}
|
}
|
||||||
@@ -178,10 +183,8 @@ impl NavigatedPage<SyncMessage> for SyncState {
|
|||||||
|
|
||||||
impl SyncState {
|
impl SyncState {
|
||||||
pub fn new(state: AppState) -> SyncState {
|
pub fn new(state: AppState) -> SyncState {
|
||||||
let auto_fetch;
|
let auto_fetch = state.sync_data.read().auto_web_fetch;
|
||||||
{
|
|
||||||
auto_fetch = state.lock().sync_data.auto_web_fetch;
|
|
||||||
}
|
|
||||||
Self {
|
Self {
|
||||||
auto_fetch,
|
auto_fetch,
|
||||||
state,
|
state,
|
||||||
@@ -190,6 +193,15 @@ impl SyncState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reload_data(&mut self) -> Result<(), NotificationData> {
|
||||||
|
self.state.card_states.write().reload()?;
|
||||||
|
self.state.word_groups.write().reload()?;
|
||||||
|
self.state.decks.write().reload()?;
|
||||||
|
self.state.dictionary.write().reload()?;
|
||||||
|
self.state.history.write().reload()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn sync_column(&self) -> Element<'_, SyncMessage> {
|
fn sync_column(&self) -> Element<'_, SyncMessage> {
|
||||||
let network_view: Element<'_, SyncMessage> = if self.frozen {
|
let network_view: Element<'_, SyncMessage> = if self.frozen {
|
||||||
progress_bar(0.0..=5.0, self.progress).into()
|
progress_bar(0.0..=5.0, self.progress).into()
|
||||||
@@ -197,7 +209,7 @@ impl SyncState {
|
|||||||
space().into()
|
space().into()
|
||||||
};
|
};
|
||||||
{
|
{
|
||||||
if let Some(key) = self.state.lock().sync_data.key.clone() {
|
if let Some(key) = self.state.sync_data.read().key.as_ref() {
|
||||||
column![
|
column![
|
||||||
text!("Ваш ключ синхронизации"),
|
text!("Ваш ключ синхронизации"),
|
||||||
container(
|
container(
|
||||||
@@ -254,13 +266,15 @@ impl SyncState {
|
|||||||
}
|
}
|
||||||
self.frozen = true;
|
self.frozen = true;
|
||||||
self.progress = 0.0;
|
self.progress = 0.0;
|
||||||
let state = self.state.lock();
|
let mut connection = self.state.connection.lock();
|
||||||
state
|
|
||||||
.connection
|
connection
|
||||||
.query_row("PRAGMA wal_checkpoint(TRUNCATE)", [], |_| Ok(()))
|
.query_row("PRAGMA wal_checkpoint(TRUNCATE)", [], |_| Ok(()))
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
error!("Ошибка при checkpoint WAL: {e}");
|
error!("Ошибка при checkpoint WAL: {e}");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connection.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user