diff --git a/src/navigation.rs b/src/navigation.rs index e4994a9..258ad34 100644 --- a/src/navigation.rs +++ b/src/navigation.rs @@ -21,7 +21,6 @@ use crate::{AppState, fill_state}; use chrono::NaiveDate; use iced::keyboard::Event; use iced::{Element, Task}; -use rusqlite::fallible_iterator::FallibleIterator; use std::collections::HashMap; use std::sync::{Arc, Mutex}; diff --git a/src/repetition.rs b/src/repetition.rs index 9f209f5..84b1207 100644 --- a/src/repetition.rs +++ b/src/repetition.rs @@ -16,6 +16,7 @@ use std::sync::{Arc, Mutex}; use tokio::task::spawn_blocking; pub struct RepetitionState { + state: Arc>, pub settings: CardSetSettings, pub set: CardSet, current_word: WordData, @@ -43,6 +44,7 @@ impl RepetitionState { let sink_handle = rodio::DeviceSinkBuilder::open_default_sink().unwrap(); RepetitionState { + state, settings: set, set: card_set, current_word: word, @@ -109,6 +111,7 @@ impl RepetitionState { |_| RootMessage::Repetition(RepetitionMessage::PlayFinished), ); } + Task::none() } diff --git a/src/repetition_settings.rs b/src/repetition_settings.rs index bb7de55..4d0cf7e 100644 --- a/src/repetition_settings.rs +++ b/src/repetition_settings.rs @@ -1,15 +1,15 @@ -use crate::AppState; use crate::data_provider::card_sets::{delete_set, update_card_set}; use crate::navigation::Page::PreviousPage; use crate::navigation::{NavigatedPage, Page, RootMessage}; use crate::repetition_settings::RepetitionSettingsMessage::*; -use crate::repetitions::{CardSetSettings, RepetitionsMessage}; +use crate::repetitions::CardSetSettings; use crate::styling::*; +use crate::AppState; use iced::widget::button::danger; use iced::widget::{button, column, row, scrollable, space, text, text_input}; use iced::{Element, Task}; use iced_core::Length::Fill; -use iced_core::{Length, Padding}; +use iced_core::Padding; use std::sync::{Arc, Mutex}; use std::time::Duration; diff --git a/src/repetitions.rs b/src/repetitions.rs index d56e99d..d368903 100644 --- a/src/repetitions.rs +++ b/src/repetitions.rs @@ -9,11 +9,17 @@ use crate::repetition_settings::RepetitionSettingsState; use crate::repetitions::RepetitionsMessage::*; use crate::styling::*; use crate::{AppState, RootMessage}; +use chrono::{Days, Local}; use iced::widget::button::{danger, Status}; pub use iced::widget::button::{Catalog, Style}; use iced::widget::container::bordered_box; -use iced::widget::{button, column, container, radio, row, scrollable, space, svg, text, text_input, Column}; +use iced::widget::tooltip::Position::Top; +use iced::widget::{ + button, column, container, radio, row, scrollable, space, svg, text, text_input, tooltip, Column, + Row, +}; use iced::{Background, Border, Center, Color, Element, Fill, Length, Shadow, Task, Theme}; +use iced_core::border::Radius; use iced_core::svg::Handle; use iced_core::Padding; use rhai::{Engine, Scope}; @@ -156,8 +162,7 @@ impl RepetitionsState { } fn launch_delete_button(&self, set: &CardSetSettings) -> Element<'_, RepetitionsMessage> { - if set.id != 0 - { + if set.id != 0 { return row![ space().width(Fill), button(text!("Начать повторение").width(200).center()) @@ -173,10 +178,9 @@ impl RepetitionsState { if let Some(index) = self.selected_set { let set; { - let state = self.state.lock().unwrap(); if state.card_sets.len() <= index { - return space().width(Length::FillPortion(3)).into() + return space().width(Length::FillPortion(3)).into(); } set = state.card_sets[index].clone(); } @@ -254,7 +258,75 @@ impl RepetitionsState { } fn filled_set_data_view(&self, set: &CardSetSettings) -> Column<'_, RepetitionsMessage> { - column![].spacing(DEFAULT_SPACING).into() + column![self.activity_bar(set)].align_x(Center) + .width(Fill) + .spacing(DEFAULT_SPACING) + .into() + } + + fn activity_bar(&self, set: &CardSetSettings) -> Element<'_, RepetitionsMessage> { + let state = self.state.lock().unwrap(); + let history = state.activity.get(&set.id); + let mut counts: Vec = vec![0; 30 * 7]; + + let now = Local::now().date_naive(); + + if let Some(history) = history { + for (date, activity) in history { + let distance = *date - now; + let index = counts.len() as i64 - distance.num_days() - 1; + + if index < 0 { + continue; + } + + counts[index as usize] = *activity; + } + } + + let mut row = Row::new() + .padding(Padding { + top: 0.0, + right: 0.0, + bottom: 0.0, + left: DEFAULT_SPACING, + }) + .spacing(QUARTER_SPACING); + + const MAX_DAY_COUNT: f32 = 128.0; + + let mut iter = counts.into_iter(); + for i in 0..30 { + let mut column = Column::new().spacing(QUARTER_SPACING); + + 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; + + let date = now.clone().checked_sub_days(Days::new(30 * 7 - i * 7 - j - 1)).unwrap(); + + 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, + }, + ), + text!("{} {} карточки", date.format("%Y.%m.%d"), value), + Top, + )); + } + + row = row.push(column); + } + scrollable(column!["Карта активности", row].spacing(QUARTER_SPACING).align_x(Center)).into() } fn words_words_view(&self, set: &CardSetSettings) -> Element<'_, RepetitionsMessage> {