2026-08-24 17:33:21 +03:00
|
|
|
use crate::app::AppState;
|
2026-09-04 22:34:36 +03:00
|
|
|
use crate::core::deck::DeckData;
|
2026-09-04 13:00:41 +03:00
|
|
|
use crate::data::entities::{DeckSettings, EntityHandle};
|
2026-09-04 22:34:36 +03:00
|
|
|
use crate::data::repositories::{BatchedRepository, Repository};
|
2026-08-04 16:53:09 +03:00
|
|
|
use crate::navigation::Page::PreviousPage;
|
|
|
|
|
use crate::navigation::{NavigatedPage, Page, RootMessage};
|
2026-08-24 23:42:58 +03:00
|
|
|
use crate::notification::MaybeError;
|
2026-08-24 01:22:00 +03:00
|
|
|
use crate::pages::repetition_settings::RepetitionSettingsMessage::*;
|
2026-08-06 11:31:11 +03:00
|
|
|
use crate::styling::*;
|
2026-08-24 18:16:41 +03:00
|
|
|
use crate::{push_error, push_error_task};
|
2026-08-06 11:31:11 +03:00
|
|
|
use iced::widget::button::danger;
|
2026-08-19 23:51:41 +03:00
|
|
|
use iced::widget::text_input::Catalog;
|
2026-08-06 11:31:11 +03:00
|
|
|
use iced::widget::{button, column, row, scrollable, space, text, text_input};
|
2026-08-04 16:53:09 +03:00
|
|
|
use iced::{Element, Task};
|
2026-08-06 11:31:11 +03:00
|
|
|
use iced_core::Length::Fill;
|
2026-08-25 12:04:07 +03:00
|
|
|
use iced_core::{Padding, Theme, color};
|
2026-08-20 00:49:49 +03:00
|
|
|
use std::sync::Arc;
|
2026-08-06 11:31:11 +03:00
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
|
|
pub struct RepetitionSettingsState {
|
2026-09-04 22:34:36 +03:00
|
|
|
settings_handle: EntityHandle<DeckSettings>,
|
|
|
|
|
settings_data: DeckSettings,
|
2026-08-31 11:46:35 +03:00
|
|
|
state: AppState,
|
2026-08-06 11:31:11 +03:00
|
|
|
real_delete: bool,
|
2026-08-18 17:45:50 +03:00
|
|
|
correct_filter: bool,
|
2026-08-20 16:22:38 +03:00
|
|
|
filter_changed: bool,
|
2026-09-04 22:34:36 +03:00
|
|
|
filtered_words: Option<u32>,
|
2026-08-06 11:31:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RepetitionSettingsState {
|
2026-09-04 22:34:36 +03:00
|
|
|
pub(crate) fn new(
|
|
|
|
|
settings: EntityHandle<DeckSettings>,
|
|
|
|
|
state: AppState,
|
|
|
|
|
) -> RepetitionSettingsState {
|
|
|
|
|
let data = state.decks.read().get_static(settings).unwrap();
|
2026-08-06 11:31:11 +03:00
|
|
|
RepetitionSettingsState {
|
2026-09-04 22:34:36 +03:00
|
|
|
settings_data: data,
|
|
|
|
|
settings_handle: settings,
|
2026-08-20 00:07:09 +03:00
|
|
|
state,
|
2026-08-06 11:31:11 +03:00
|
|
|
real_delete: false,
|
2026-09-07 15:49:39 +03:00
|
|
|
correct_filter: true,
|
2026-08-20 16:22:38 +03:00
|
|
|
filter_changed: false,
|
2026-09-04 22:34:36 +03:00
|
|
|
filtered_words: None,
|
2026-08-06 11:31:11 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-08-04 16:53:09 +03:00
|
|
|
|
|
|
|
|
impl NavigatedPage<RepetitionSettingsMessage> for RepetitionSettingsState {
|
2026-08-18 12:14:35 +03:00
|
|
|
fn navigate(&mut self, message: &RepetitionSettingsMessage) -> Option<Page> {
|
2026-08-04 16:53:09 +03:00
|
|
|
if let Back = message {
|
|
|
|
|
Some(PreviousPage)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-08-06 23:20:14 +03:00
|
|
|
|
2026-08-15 21:32:24 +03:00
|
|
|
fn navigated(&mut self) {}
|
2026-08-13 22:03:47 +03:00
|
|
|
fn update(&mut self, message: RepetitionSettingsMessage) -> Task<RootMessage> {
|
2026-08-04 16:53:09 +03:00
|
|
|
match message {
|
|
|
|
|
Back => {}
|
2026-08-06 11:31:11 +03:00
|
|
|
Save => {
|
2026-09-04 22:34:36 +03:00
|
|
|
let mut decks = self.state.decks.write();
|
|
|
|
|
decks.set(&mut self.settings_handle, self.settings_data.clone());
|
|
|
|
|
|
|
|
|
|
if let Err(e) = decks.save(&mut self.settings_handle) {
|
2026-08-25 12:04:07 +03:00
|
|
|
return push_error_task!(e);
|
2026-08-24 17:33:21 +03:00
|
|
|
}
|
2026-09-04 22:34:36 +03:00
|
|
|
drop(decks);
|
2026-08-24 18:16:41 +03:00
|
|
|
if self.filter_changed
|
2026-09-04 22:34:36 +03:00
|
|
|
&& let Err(e) = update_deck_stats(&self.settings_data, &self.state)
|
2026-08-24 18:16:41 +03:00
|
|
|
{
|
2026-08-25 12:04:07 +03:00
|
|
|
return push_error_task!(e);
|
2026-08-20 16:22:38 +03:00
|
|
|
}
|
2026-08-06 11:31:11 +03:00
|
|
|
}
|
|
|
|
|
SetName(new) => {
|
2026-09-04 22:34:36 +03:00
|
|
|
self.settings_data.name = new;
|
2026-08-06 11:31:11 +03:00
|
|
|
}
|
|
|
|
|
SetForward(new) => {
|
2026-09-04 22:34:36 +03:00
|
|
|
self.settings_data.forward = new;
|
2026-08-06 11:31:11 +03:00
|
|
|
}
|
|
|
|
|
SetBackward(backward) => {
|
2026-09-04 22:34:36 +03:00
|
|
|
self.settings_data.backward = backward;
|
2026-08-06 11:31:11 +03:00
|
|
|
}
|
|
|
|
|
SetFilter(new) => {
|
2026-09-04 22:34:36 +03:00
|
|
|
self.settings_data.filter = new;
|
|
|
|
|
self.correct_filter = self.settings_data.check_filter();
|
2026-08-20 16:22:38 +03:00
|
|
|
self.filter_changed = true;
|
2026-08-06 11:31:11 +03:00
|
|
|
}
|
2026-09-06 23:55:50 +03:00
|
|
|
TryFilter => {
|
|
|
|
|
let list = DeckData::list(&self.settings_data, &self.state);
|
|
|
|
|
self.filtered_words = Some(list.len() as u32);
|
|
|
|
|
}
|
2026-08-06 11:31:11 +03:00
|
|
|
DeleteSet => {
|
2026-08-15 13:22:59 +03:00
|
|
|
if !self.real_delete {
|
2026-08-06 11:31:11 +03:00
|
|
|
self.real_delete = true;
|
|
|
|
|
return Task::future(async {
|
|
|
|
|
tokio::time::sleep(Duration::from_millis(3000)).await;
|
2026-08-15 13:22:59 +03:00
|
|
|
RootMessage::RepetitionSettings(RevertDeleteSet)
|
2026-08-06 11:31:11 +03:00
|
|
|
});
|
|
|
|
|
}
|
2026-09-04 22:34:36 +03:00
|
|
|
let mut decks = self.state.decks.write();
|
|
|
|
|
if let Err(e) = decks.delete(self.settings_handle) {
|
2026-08-24 18:16:41 +03:00
|
|
|
return push_error_task!(e);
|
2026-08-24 17:33:21 +03:00
|
|
|
}
|
|
|
|
|
|
2026-08-06 11:31:11 +03:00
|
|
|
return Task::done(RootMessage::RepetitionSettings(Back));
|
2026-08-15 21:32:24 +03:00
|
|
|
}
|
2026-08-06 11:31:11 +03:00
|
|
|
RevertDeleteSet => self.real_delete = false,
|
2026-08-04 16:53:09 +03:00
|
|
|
}
|
|
|
|
|
Task::none()
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-13 22:03:47 +03:00
|
|
|
fn view(&self) -> Element<'_, RepetitionSettingsMessage> {
|
2026-08-04 16:53:09 +03:00
|
|
|
back_overlay(
|
2026-08-06 11:31:11 +03:00
|
|
|
iced::widget::column![
|
|
|
|
|
scrollable(
|
|
|
|
|
column![
|
|
|
|
|
column![
|
|
|
|
|
text!("Название набора"),
|
2026-09-04 22:34:36 +03:00
|
|
|
text_input("", &self.settings_data.name).on_input(SetName),
|
2026-08-06 11:31:11 +03:00
|
|
|
]
|
|
|
|
|
.spacing(QUARTER_SPACING),
|
|
|
|
|
column![
|
|
|
|
|
text!("Передняя сторона"),
|
2026-09-04 22:34:36 +03:00
|
|
|
text_input("", &self.settings_data.forward).on_input(SetForward),
|
2026-08-06 11:31:11 +03:00
|
|
|
]
|
|
|
|
|
.spacing(QUARTER_SPACING),
|
|
|
|
|
column![
|
|
|
|
|
text!("Задняя сторона"),
|
2026-09-04 22:34:36 +03:00
|
|
|
text_input("", &self.settings_data.backward).on_input(SetBackward),
|
2026-08-06 11:31:11 +03:00
|
|
|
]
|
|
|
|
|
.spacing(QUARTER_SPACING),
|
|
|
|
|
column![
|
|
|
|
|
text!("Фильтр"),
|
2026-09-04 22:34:36 +03:00
|
|
|
text_input("", &self.settings_data.filter)
|
2026-08-19 23:51:41 +03:00
|
|
|
.style(|x, status| {
|
|
|
|
|
let mut primary = Theme::default()(x, status);
|
|
|
|
|
if !self.correct_filter {
|
|
|
|
|
primary.border.color = color!(255, 255, 0);
|
|
|
|
|
}
|
|
|
|
|
primary
|
|
|
|
|
})
|
|
|
|
|
.on_input(SetFilter),
|
2026-08-06 11:31:11 +03:00
|
|
|
button("Проверить фильтр")
|
|
|
|
|
.style(jl_button)
|
|
|
|
|
.on_press(TryFilter),
|
2026-09-04 22:34:36 +03:00
|
|
|
self.count_view(),
|
2026-08-06 11:31:11 +03:00
|
|
|
]
|
|
|
|
|
.spacing(QUARTER_SPACING),
|
|
|
|
|
]
|
|
|
|
|
.spacing(DEFAULT_SPACING)
|
|
|
|
|
.padding(Padding {
|
|
|
|
|
top: 0.0,
|
|
|
|
|
right: DEFAULT_SPACING,
|
|
|
|
|
bottom: 0.0,
|
|
|
|
|
left: 0.0,
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
.height(Fill),
|
|
|
|
|
row![
|
|
|
|
|
button("Сохранить").style(jl_button).on_press(Save),
|
|
|
|
|
button(if self.real_delete {
|
|
|
|
|
"Вы уверены?"
|
|
|
|
|
} else {
|
|
|
|
|
"Удалить"
|
|
|
|
|
})
|
|
|
|
|
.style(danger)
|
|
|
|
|
.on_press(DeleteSet),
|
|
|
|
|
space().width(Fill),
|
|
|
|
|
]
|
|
|
|
|
.spacing(DEFAULT_SPACING),
|
|
|
|
|
]
|
2026-08-15 21:32:24 +03:00
|
|
|
.spacing(DEFAULT_SPACING)
|
|
|
|
|
.into(),
|
2026-08-04 16:53:09 +03:00
|
|
|
Back,
|
|
|
|
|
)
|
|
|
|
|
}
|
2026-08-13 22:03:47 +03:00
|
|
|
}
|
|
|
|
|
|
2026-08-24 23:42:58 +03:00
|
|
|
fn update_deck_stats(deck: &DeckSettings, state: &AppState) -> MaybeError {
|
2026-09-04 22:34:36 +03:00
|
|
|
let states_repo = &mut state.card_states.write();
|
|
|
|
|
let deck_id = deck.id;
|
|
|
|
|
let mut db_stats = states_repo
|
2026-09-06 23:55:50 +03:00
|
|
|
.query_par(Arc::new(move |s| s.deck_id == deck_id))
|
2026-09-04 22:34:36 +03:00
|
|
|
.iter(&states_repo)
|
|
|
|
|
.collect::<Vec<_>>();
|
2026-09-06 23:55:50 +03:00
|
|
|
let filtered_words = DeckData::list(deck, state)
|
2026-09-04 22:34:36 +03:00
|
|
|
.iter()
|
|
|
|
|
.map(|w| w.id)
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
filtered_words.iter().for_each(|word_id| {
|
|
|
|
|
if let Some(index) = db_stats.iter().position(|stat_id| stat_id.id == *word_id) {
|
2026-08-20 16:22:38 +03:00
|
|
|
db_stats.remove(index);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2026-09-04 22:34:36 +03:00
|
|
|
let mut delete_operation = db_stats
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|state| EntityHandle::new(state.id, state.id.0 as usize, true))
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
states_repo.delete_many(delete_operation.as_mut_slice())?;
|
|
|
|
|
|
2026-08-24 18:16:41 +03:00
|
|
|
Ok(())
|
2026-08-20 16:22:38 +03:00
|
|
|
}
|
|
|
|
|
|
2026-08-13 22:03:47 +03:00
|
|
|
impl RepetitionSettingsState {
|
2026-09-04 22:34:36 +03:00
|
|
|
fn count_view(&self) -> Element<'_, RepetitionSettingsMessage> {
|
2026-09-04 13:00:41 +03:00
|
|
|
if let Some(count) = self.filtered_words {
|
2026-08-06 11:31:11 +03:00
|
|
|
return text!("Количество слов: {}", count).into();
|
|
|
|
|
}
|
|
|
|
|
space().into()
|
|
|
|
|
}
|
2026-08-04 16:53:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
2026-08-06 11:31:11 +03:00
|
|
|
pub enum RepetitionSettingsMessage {
|
|
|
|
|
Back,
|
|
|
|
|
Save,
|
|
|
|
|
SetName(String),
|
|
|
|
|
SetForward(String),
|
|
|
|
|
SetBackward(String),
|
|
|
|
|
SetFilter(String),
|
|
|
|
|
TryFilter,
|
|
|
|
|
DeleteSet,
|
|
|
|
|
RevertDeleteSet,
|
|
|
|
|
}
|