2026-04-13 22:25:58 +03:00
use crate ::data_provider ::card_sets ::{ delete_set , update_card_set };
2026-08-01 17:16:58 +03:00
use crate ::history ::HistoryState ;
2026-08-15 20:16:48 +03:00
use crate ::lang ::{ AppendMode , CardSetSettings , OrderMode };
2026-08-04 16:53:09 +03:00
use crate ::navigation ::Page ::{ History , PreviousPage , Repetition , RepetitionSettings };
2026-08-01 17:16:58 +03:00
use crate ::navigation ::{ NavigatedPage , Page };
2026-04-13 22:25:58 +03:00
use crate ::repetition ::RepetitionState ;
2026-08-06 11:31:11 +03:00
use crate ::repetition_settings ::RepetitionSettingsState ;
use crate ::repetitions ::RepetitionsMessage ::* ;
2026-08-01 17:16:58 +03:00
use crate ::styling ::* ;
2026-08-01 13:04:18 +03:00
use crate ::{ AppState , RootMessage };
2026-08-06 19:12:23 +03:00
use chrono ::{ Days , Local };
2026-08-06 11:31:11 +03:00
use iced ::widget ::button ::{ danger , Status };
2026-04-13 22:25:58 +03:00
pub use iced ::widget ::button ::{ Catalog , Style };
2026-05-25 12:23:32 +03:00
use iced ::widget ::container ::bordered_box ;
2026-08-06 19:12:23 +03:00
use iced ::widget ::tooltip ::Position ::Top ;
2026-08-09 14:34:38 +03:00
use iced ::widget ::{ button , column , container , lazy , radio , row , scrollable , space , svg , text , text_input , tooltip , Column , Row };
2026-08-01 19:36:43 +03:00
use iced ::{ Background , Border , Center , Color , Element , Fill , Length , Shadow , Task , Theme };
2026-08-06 19:12:23 +03:00
use iced_core ::border ::Radius ;
2026-08-04 16:53:09 +03:00
use iced_core ::svg ::Handle ;
2026-08-06 11:31:11 +03:00
use iced_core ::Padding ;
2026-04-09 22:45:31 +03:00
use std ::sync ::{ Arc , Mutex };
#[derive(Clone)]
pub struct RepetitionsState {
selected_set : Option < usize > ,
correct_filters : Vec < bool > ,
pub state : Arc < Mutex < AppState >> ,
}
impl NavigatedPage < RepetitionsMessage > for RepetitionsState {
fn navigate ( & self , message : & RepetitionsMessage ) -> Option < Page > {
2026-08-04 16:53:09 +03:00
if let Back = message {
2026-04-09 22:45:31 +03:00
Some ( PreviousPage )
2026-08-04 16:53:09 +03:00
} else if let GoToHistory = message {
2026-07-29 12:57:46 +03:00
let card_set ;
{
card_set = self . state . lock (). unwrap (). card_sets [ self . selected_set . unwrap ()]. id ;
}
Some ( History ( HistoryState ::new ( card_set , self . state . clone ())))
2026-08-04 16:53:09 +03:00
} else if let GoToRepetition = message {
2026-04-13 22:25:58 +03:00
let clone = self . state . clone ();
2026-04-14 15:19:46 +03:00
let card_set ;
{
card_set = self . state . lock (). unwrap (). card_sets [ self . selected_set . unwrap ()]. clone ();
}
2026-05-25 12:23:32 +03:00
Some ( Repetition ( RepetitionState ::new ( card_set , clone )))
2026-08-06 11:31:11 +03:00
} else if let GoToSettings = message {
Some ( RepetitionSettings ( RepetitionSettingsState ::new (
self . selected_set . unwrap (),
self . state . clone (),
)))
} else {
2026-04-09 22:45:31 +03:00
None
}
}
2026-08-06 23:20:14 +03:00
fn navigated ( & mut self ) {
self . selected_set = None ;
}
2026-08-13 22:03:47 +03:00
fn update ( & mut self , message : RepetitionsMessage ) -> Task < RootMessage > {
2026-04-13 22:25:58 +03:00
let mut state = self . state . lock (). unwrap ();
2026-04-09 22:45:31 +03:00
match message {
2026-08-04 16:53:09 +03:00
Next => {}
Back => {}
GoToRepetition => {}
GoToHistory => {}
GoToSettings => {}
CreateSet => {
2026-04-13 22:25:58 +03:00
let index = state . card_sets . len () + 1 ;
state
. card_sets
. push ( CardSetSettings ::with_name ( format! ( "Card set # {} " , index )));
2026-04-09 22:45:31 +03:00
self . correct_filters . push ( true );
}
2026-08-04 16:53:09 +03:00
DeleteSet => {
2026-04-13 22:25:58 +03:00
let set = state . card_sets . remove ( self . selected_set . unwrap ());
2026-04-09 22:45:31 +03:00
self . correct_filters . remove ( self . selected_set . unwrap ());
self . selected_set = None ;
2026-04-13 22:25:58 +03:00
delete_set ( & set , & state . connection );
2026-04-09 22:45:31 +03:00
}
2026-08-04 16:53:09 +03:00
SelectSet ( index ) => {
2026-04-09 22:45:31 +03:00
self . selected_set = Some ( index );
2026-05-25 12:23:32 +03:00
let mut set = state . card_sets . get ( index ). unwrap (). clone ();
set . update_worst_words ( & state );
state . card_sets [ index ] = set ;
2026-04-09 22:45:31 +03:00
}
2026-08-04 16:53:09 +03:00
SetName ( new ) => {
2026-04-13 22:25:58 +03:00
state . card_sets [ self . selected_set . unwrap ()]. name = new ;
2026-04-09 22:45:31 +03:00
}
2026-08-04 16:53:09 +03:00
Save => {
2026-04-13 22:25:58 +03:00
for i in 0 .. state . card_sets . len () {
self . correct_filters [ i ] = state . card_sets [ i ]. check_filter ();
2026-04-09 22:45:31 +03:00
}
2026-08-06 11:31:11 +03:00
let set = & mut state . card_sets [ self . selected_set . unwrap ()]. clone ();
2026-04-13 22:25:58 +03:00
2026-08-06 11:31:11 +03:00
update_card_set ( set , & state . connection );
2026-04-13 22:25:58 +03:00
2026-08-06 11:31:11 +03:00
state . card_sets [ self . selected_set . unwrap ()] = set . clone ();
2026-04-09 22:45:31 +03:00
}
2026-08-04 16:53:09 +03:00
SetForward ( new ) => {
2026-04-13 22:25:58 +03:00
state . card_sets [ self . selected_set . unwrap ()]. forward = new ;
2026-04-09 22:45:31 +03:00
}
2026-08-04 16:53:09 +03:00
SetBackward ( new ) => {
2026-04-13 22:25:58 +03:00
state . card_sets [ self . selected_set . unwrap ()]. backward = new ;
2026-04-09 22:45:31 +03:00
}
2026-08-04 16:53:09 +03:00
SetFilter ( new ) => {
2026-04-13 22:25:58 +03:00
state . card_sets [ self . selected_set . unwrap ()]. filter = new ;
2026-04-09 22:45:31 +03:00
}
2026-08-04 16:53:09 +03:00
TryFilter => {
2026-04-13 22:25:58 +03:00
let set = state . card_sets . get ( self . selected_set . unwrap ()). unwrap ();
let count = set . get_word_list ( & state ). len ();
state . card_sets [ self . selected_set . unwrap ()]. count = Some ( count );
2026-08-01 19:43:07 +03:00
}
2026-08-04 16:53:09 +03:00
SetOpenMode ( mode ) => {
2026-08-01 19:43:07 +03:00
state
. card_sets
. get_mut ( self . selected_set . unwrap ())
. unwrap ()
. open_mode = mode ;
2026-08-06 11:31:11 +03:00
}
2026-08-15 20:16:48 +03:00
SetAppendMode ( mode ) => {
let set = state . card_sets . get_mut ( self . selected_set . unwrap ()). unwrap ();
set . append_mode = mode ;
}
2026-04-09 22:45:31 +03:00
}
Task ::none ()
}
2026-08-13 22:03:47 +03:00
fn view ( & self ) -> Element < '_ , RepetitionsMessage > {
2026-08-01 19:43:07 +03:00
back_overlay (
row! [
column! [
scrollable ( self . sets_list ()). height ( Fill ),
button ( "Добавить" )
. style ( jl_button )
. width ( Fill )
2026-08-04 16:53:09 +03:00
. on_press ( CreateSet ),
2026-04-09 22:45:31 +03:00
]
2026-08-01 19:43:07 +03:00
. spacing ( DEFAULT_SPACING )
. width ( Length ::FillPortion ( 1 )),
self . selected_set_view (),
]
2026-08-13 22:03:47 +03:00
. align_y ( Center )
. spacing ( DEFAULT_SPACING )
. width ( Fill )
. height ( Fill )
. into (),
2026-08-04 16:53:09 +03:00
Back ,
2026-08-01 19:43:07 +03:00
)
2026-04-09 22:45:31 +03:00
}
2026-08-13 22:03:47 +03:00
}
impl RepetitionsState {
pub ( crate ) fn new ( state : Arc < Mutex < AppState >> ) -> RepetitionsState {
let count = state . lock (). unwrap (). card_sets . len ();
RepetitionsState {
selected_set : None ,
correct_filters : vec ! [ true ; count ],
state ,
}
}
}
impl RepetitionsState {
2026-08-06 11:31:11 +03:00
fn launch_delete_button ( & self , set : & CardSetSettings ) -> Element < '_ , RepetitionsMessage > {
2026-08-06 19:12:23 +03:00
if set . id != 0 {
2026-08-06 11:31:11 +03:00
return row! [
space (). width ( Fill ),
button ( text! ( "Начать повторение" ). width ( 200 ). center ())
. style ( cta_button )
. on_press ( GoToRepetition )
]
. into ();
2026-04-09 22:45:31 +03:00
}
2026-08-06 11:31:11 +03:00
button ( "Удалить" ). style ( danger ). on_press ( DeleteSet ). into ()
2026-04-09 22:45:31 +03:00
}
fn selected_set_view ( & self ) -> Element < '_ , RepetitionsMessage > {
if let Some ( index ) = self . selected_set {
2026-05-25 12:23:32 +03:00
let set ;
{
2026-08-06 11:31:11 +03:00
let state = self . state . lock (). unwrap ();
if state . card_sets . len () <= index {
2026-08-06 19:12:23 +03:00
return space (). width ( Length ::FillPortion ( 3 )). into ();
2026-08-06 11:31:11 +03:00
}
set = state . card_sets [ index ]. clone ();
2026-05-25 12:23:32 +03:00
}
2026-04-09 22:45:31 +03:00
return column! [
scrollable (
column! [
2026-08-04 14:09:40 +03:00
column! [
text! ( "Название набора" ),
2026-08-04 16:53:09 +03:00
row! [
text_input ( "" , & set . name ). on_input ( SetName ),
2026-08-06 11:31:11 +03:00
button ( svg ( Handle ::from_memory ( SETTINGS_ICON )))
. width ( 43 )
. on_press ( GoToSettings ),
2026-08-04 16:53:09 +03:00
]
. spacing ( HALF_SPACING )
2026-08-04 14:09:40 +03:00
]
. spacing ( QUARTER_SPACING ),
2026-08-06 11:31:11 +03:00
{
if set . id == 0 {
column! [
column! [
text! ( "Передняя сторона" ),
text_input ( "" , & set . forward ). on_input ( SetForward ),
]
. spacing ( QUARTER_SPACING ),
column! [
text! ( "Задняя сторона" ),
text_input ( "" , & set . backward ). on_input ( SetBackward ),
]
. spacing ( QUARTER_SPACING ),
column! [
text! ( "Фильтр" ),
text_input ( "" , & set . filter ). on_input ( SetFilter ),
button ( "Проверить фильтр" )
. style ( jl_button )
. on_press ( TryFilter ),
self . count_view ( & set ),
]
. spacing ( QUARTER_SPACING ),
]
. spacing ( DEFAULT_SPACING )
} else {
self . filled_set_data_view ( & set )
}
},
2026-08-01 19:43:07 +03:00
radio (
"Обычный режим" ,
2026-08-15 20:16:48 +03:00
OrderMode ::Default ,
2026-08-01 19:43:07 +03:00
Some ( set . open_mode ),
2026-08-04 16:53:09 +03:00
SetOpenMode
2026-08-01 19:43:07 +03:00
),
2026-08-15 20:16:48 +03:00
// self.words_words_view(&set),
self . word_append_panel ( & set ),
2026-08-06 11:31:11 +03:00
button ( "История" ). style ( jl_button ). on_press ( GoToHistory ),
2026-04-09 22:45:31 +03:00
]
2026-04-16 09:56:57 +03:00
. spacing ( DEFAULT_SPACING )
2026-08-04 14:09:40 +03:00
. padding ( Padding {
top : 0.0 ,
right : DEFAULT_SPACING ,
bottom : 0.0 ,
left : 0.0 ,
})
2026-04-09 22:45:31 +03:00
)
. height ( Fill ),
2026-04-13 22:25:58 +03:00
row! [
2026-08-06 11:31:11 +03:00
button ( "Сохранить" ). style ( jl_button ). on_press ( Save ),
self . launch_delete_button ( & set )
2026-04-13 22:25:58 +03:00
]
2026-04-16 09:56:57 +03:00
. spacing ( DEFAULT_SPACING ),
2026-04-09 22:45:31 +03:00
]
2026-04-16 09:56:57 +03:00
. spacing ( DEFAULT_SPACING )
2026-08-04 14:09:40 +03:00
. width ( Length ::FillPortion ( 3 ))
2026-04-13 22:25:58 +03:00
. into ();
2026-04-09 22:45:31 +03:00
}
2026-08-04 14:09:40 +03:00
space (). width ( Length ::FillPortion ( 3 )). into ()
2026-04-09 22:45:31 +03:00
}
2026-08-06 11:31:11 +03:00
fn filled_set_data_view ( & self , set : & CardSetSettings ) -> Column < '_ , RepetitionsMessage > {
2026-08-06 19:12:23 +03:00
column! [ self . activity_bar ( set )]. align_x ( Center )
. width ( Fill )
. spacing ( DEFAULT_SPACING )
}
fn activity_bar ( & self , set : & CardSetSettings ) -> Element < '_ , RepetitionsMessage > {
2026-08-09 14:34:38 +03:00
const MAX_DAY_COUNT : f32 = 128.0 ;
2026-08-06 19:12:23 +03:00
let state = self . state . lock (). unwrap ();
let history = state . activity . get ( & set . id );
let mut counts : Vec < u32 > = vec! [ 0 ; 30 * 7 ];
let now = Local ::now (). date_naive ();
2026-08-09 14:34:38 +03:00
2026-08-06 19:12:23 +03:00
if let Some ( history ) = history {
for ( date , activity ) in history {
2026-08-06 23:20:14 +03:00
let distance = now - * date ;
2026-08-06 19:12:23 +03:00
let index = counts . len () as i64 - distance . num_days () - 1 ;
2026-08-06 23:20:14 +03:00
if index == 210 {
println! ( "invalid index" )
}
2026-08-06 19:12:23 +03:00
if index < 0 {
continue ;
}
counts [ index as usize ] = * activity ;
}
}
2026-08-09 14:34:38 +03:00
lazy (( counts , now ), | ( counts , now ) | {
let mut row = Row ::new ()
. padding ( Padding {
top : 0.0 ,
right : 0.0 ,
bottom : 0.0 ,
left : DEFAULT_SPACING ,
})
. spacing ( QUARTER_SPACING );
2026-08-06 19:12:23 +03:00
2026-08-15 13:22:59 +03:00
let mut iter = counts . iter ();
2026-08-09 14:34:38 +03:00
for i in 0 .. 30 {
let mut column = Column ::new (). spacing ( QUARTER_SPACING );
2026-08-06 19:12:23 +03:00
2026-08-09 14:34:38 +03:00
for j in 0 .. 7 {
let value = * iter . next (). unwrap () as f32 ;
let k = ( value / MAX_DAY_COUNT ). min ( 1.0 ) * 0.9 + 0.1 ;
2026-08-06 19:12:23 +03:00
2026-08-15 13:22:59 +03:00
let date = ( * now ). checked_sub_days ( Days ::new ( 30 * 7 - i * 7 - j - 1 )). unwrap ();
2026-08-06 19:12:23 +03:00
2026-08-09 14:34:38 +03:00
column = column . push ( tooltip (
iced ::widget ::container ( space (). height ( 15 ). width ( 15 )). style (
move | x : & Theme | container ::Style {
text_color : None ,
background : Some ( Background ::Color ( x . palette (). primary . scale_alpha ( k ))),
border : Border {
color : Default ::default (),
width : 0.0 ,
radius : Radius ::new ( 5 ),
},
shadow : Default ::default (),
snap : false ,
2026-08-06 19:12:23 +03:00
},
2026-08-09 14:34:38 +03:00
),
text! ( "{} {} карточки" , date . format ( "%Y.%m.%d" ), value ),
Top ,
));
}
row = row . push ( column );
2026-08-06 19:12:23 +03:00
}
2026-08-09 14:34:38 +03:00
scrollable ( column! [ "Карта активности" , row ]. spacing ( QUARTER_SPACING ). align_x ( Center ))
}). into ()
2026-08-06 19:12:23 +03:00
2026-08-06 11:31:11 +03:00
}
2026-05-25 12:23:32 +03:00
fn words_words_view ( & self , set : & CardSetSettings ) -> Element < '_ , RepetitionsMessage > {
column! [
text! ( "Худшие слова" ),
2026-08-15 13:22:59 +03:00
container ( scrollable ( self . worst_words_list ( set )). height ( 200 )). style ( bordered_box ),
2026-08-01 19:43:07 +03:00
radio (
"Начать с плохих слов" ,
2026-08-15 20:16:48 +03:00
OrderMode ::TrainWorstFirst ,
2026-08-01 19:43:07 +03:00
Some ( set . open_mode ),
2026-08-04 16:53:09 +03:00
SetOpenMode
2026-08-01 19:43:07 +03:00
),
radio (
"Полностью случайно" ,
2026-08-15 20:16:48 +03:00
OrderMode ::FullRandom ,
2026-08-01 19:43:07 +03:00
Some ( set . open_mode ),
2026-08-04 16:53:09 +03:00
SetOpenMode
2026-08-01 19:43:07 +03:00
)
2026-05-25 12:23:32 +03:00
]
. spacing ( DEFAULT_SPACING )
. into ()
}
2026-08-15 20:16:48 +03:00
fn word_append_panel ( & self , set : & CardSetSettings ) -> Element < '_ , RepetitionsMessage > {
column! [
text! ( "Режим добавления карточек" ),
row! [ radio ( "Добавлять все доступные" , AppendMode ::Full , Some ( set . append_mode ), SetAppendMode ), radio ( "Добавлять вручную" , AppendMode ::Manual , Some ( set . append_mode ), SetAppendMode )]. spacing ( HALF_SPACING ),
]. spacing ( HALF_SPACING ). into ()
}
2026-05-25 12:23:32 +03:00
fn worst_words_list ( & self , set : & CardSetSettings ) -> Element < '_ , RepetitionsMessage > {
let mut column = Column ::new ();
for word in set . worst_words_list . clone (). unwrap () {
2026-08-01 01:22:26 +03:00
column = column . push ( text! ( "{} | {}" , & word . key , & word . value ));
2026-05-25 12:23:32 +03:00
}
column . into ()
}
2026-04-13 22:25:58 +03:00
fn count_view ( & self , set : & CardSetSettings ) -> Element < '_ , RepetitionsMessage > {
if let Some ( count ) = set . count {
2026-08-01 19:36:43 +03:00
return text! ( "Количество слов: {}" , count ). into ();
2026-04-09 22:45:31 +03:00
}
space (). into ()
}
fn sets_list ( & self ) -> Column < '_ , RepetitionsMessage > {
let mut column = Column ::new ();
2026-08-15 13:22:59 +03:00
2026-04-13 22:25:58 +03:00
let sets = & self . state . lock (). unwrap (). card_sets ;
2026-08-15 13:22:59 +03:00
for ( i , set ) in sets . iter (). enumerate () {
2026-04-09 22:45:31 +03:00
column = column . push (
button ( text! ( "{}" , set . name . clone ()))
2026-08-15 13:22:59 +03:00
. on_press_with ( move || SelectSet ( i ))
2026-08-01 19:43:07 +03:00
. style ( move | _x : & Theme , status : Status | Style {
background : if status == Status ::Hovered {
2026-08-04 14:09:40 +03:00
Some ( Background ::Color ( Color ::WHITE . scale_alpha ( 0.2 )))
2026-08-01 19:43:07 +03:00
} else {
None
},
2026-08-15 13:22:59 +03:00
text_color : if self . correct_filters [ i ] {
2026-05-12 16:57:05 +03:00
_x . palette (). primary
2026-04-09 22:45:31 +03:00
} else {
_x . palette (). warning
},
2026-08-01 19:43:07 +03:00
border : Border {
2026-08-01 13:04:18 +03:00
color : Default ::default (),
width : 0.0 ,
2026-08-04 14:09:40 +03:00
radius : BUTTON_RADIUS . into (),
2026-08-01 13:04:18 +03:00
},
2026-04-09 22:45:31 +03:00
shadow : Shadow ::default (),
snap : false ,
}),
);
}
column
}
}
2026-04-14 15:19:46 +03:00
2026-05-25 12:23:32 +03:00
#[derive(Clone)]
2026-04-09 22:45:31 +03:00
pub enum RepetitionsMessage {
Next ,
Back ,
GoToRepetition ,
CreateSet ,
DeleteSet ,
SetName ( String ),
SelectSet ( usize ),
Save ,
SetForward ( String ),
SetBackward ( String ),
SetFilter ( String ),
TryFilter ,
2026-08-15 20:16:48 +03:00
SetOpenMode ( OrderMode ),
SetAppendMode ( AppendMode ),
2026-07-05 01:58:32 +03:00
GoToHistory ,
2026-08-04 16:53:09 +03:00
GoToSettings ,
2026-04-09 22:45:31 +03:00
}