Notification base

This commit is contained in:
micialware committed 2026-08-23 21:00:48 +03:00
1 parent dfaff30efa
commit e1bfd9494d
4 files changed
+177 -9

No files matched your search

+1
View File
@@ -16,6 +16,7 @@ pub mod styling;
mod sync; mod sync;
mod word; mod word;
mod writing; mod writing;
pub mod notification;
use crate::RootMessage::Keyboard; use crate::RootMessage::Keyboard;
use crate::data_provider::card_sets::load_sets; use crate::data_provider::card_sets::load_sets;
+23 -9
View File
@@ -1,14 +1,15 @@
use crate::data_provider::history::{get_history_of_set, history_dir}; use crate::data_provider::history::{get_history_of_set, history_dir};
use crate::data_provider::sqlite::{create_db, default_connection}; use crate::data_provider::sqlite::{create_db, default_connection};
use crate::data_provider::web_api::{get_local_version, get_web_version, load_data, set_local_version}; use crate::data_provider::web_api::{get_local_version, get_web_version, load_data, set_local_version};
use crate::dictionary::{DictionaryMessage, DictionaryState, app_data_dir}; use crate::dictionary::{app_data_dir, DictionaryMessage, DictionaryState};
use crate::dictionary_quiz::{DictionaryQuizMessage, DictionaryQuizState}; use crate::dictionary_quiz::{DictionaryQuizMessage, DictionaryQuizState};
use crate::history::{HistoryMessage, HistoryState}; use crate::history::{HistoryMessage, HistoryState};
use crate::import::{ImportMessage, ImportState}; use crate::import::{ImportMessage, ImportState};
use crate::lang::Id; use crate::lang::Id;
use crate::message_navigation; use crate::message_navigation;
use crate::navigation::Page::*; use crate::navigation::Page::*;
use crate::navigation::RootMessage::{DataLoaded, Keyboard, UpdateData}; use crate::navigation::RootMessage::{DataLoaded, Keyboard, PushNotification, UpdateData, Notification};
use crate::notification::{NotificationRequest, NotificationManager, NotificationMessage};
use crate::quiz::{QuizMessage, QuizState}; use crate::quiz::{QuizMessage, QuizState};
use crate::randomizer::{RandomizerMessage, RandomizerState}; use crate::randomizer::{RandomizerMessage, RandomizerState};
use crate::repetition::{RepetitionMessage, RepetitionState}; use crate::repetition::{RepetitionMessage, RepetitionState};
@@ -21,16 +22,18 @@ use crate::sync::{SyncMessage, SyncState};
use crate::view_navigation; use crate::view_navigation;
use crate::word::{WordMessage, WordState}; use crate::word::{WordMessage, WordState};
use crate::writing::{WritingMessage, WritingState}; use crate::writing::{WritingMessage, WritingState};
use crate::{AppState, fill_state}; use crate::{fill_state, AppState};
use chrono::NaiveDate; use chrono::NaiveDate;
use hashbrown::HashMap; use hashbrown::HashMap;
use iced::keyboard::Event; use iced::keyboard::Event;
use iced::widget::stack;
use iced::{Element, Task}; use iced::{Element, Task};
use parking_lot::Mutex; use parking_lot::Mutex;
use reqwest::Error; use reqwest::Error;
use rusqlite::Connection; use rusqlite::Connection;
use std::sync::Arc; use std::sync::Arc;
use std::time::Instant; use std::time::Instant;
use iced_core::Length::Fill;
impl Default for ScreenState { impl Default for ScreenState {
fn default() -> Self { fn default() -> Self {
@@ -40,6 +43,7 @@ impl Default for ScreenState {
ScreenState { ScreenState {
stack: vec![Selector(SelectorState::new(state.clone()))], stack: vec![Selector(SelectorState::new(state.clone()))],
app_state: state.clone(), app_state: state.clone(),
notification_manager: NotificationManager::new(),
} }
} }
} }
@@ -61,6 +65,8 @@ pub enum RootMessage {
Import(ImportMessage), Import(ImportMessage),
Keyboard(Event), Keyboard(Event),
DataLoaded(HashMap<Id, Vec<(NaiveDate, u32)>>), DataLoaded(HashMap<Id, Vec<(NaiveDate, u32)>>),
PushNotification(NotificationRequest),
Notification(NotificationMessage),
None, None,
UpdateData, UpdateData,
} }
@@ -85,6 +91,7 @@ pub enum Page {
pub struct ScreenState { pub struct ScreenState {
stack: Vec<Page>, stack: Vec<Page>,
app_state: Arc<Mutex<AppState>>, app_state: Arc<Mutex<AppState>>,
notification_manager: NotificationManager
} }
impl ScreenState { impl ScreenState {
@@ -187,6 +194,15 @@ impl ScreenState {
fill_state(&mut state); fill_state(&mut state);
return Task::none(); return Task::none();
} }
if let Notification(message) = message {
return self.notification_manager.update(message);
}
if let PushNotification(notification) = &message {
return self.notification_manager.push(notification.clone())
}
let task: (Task<RootMessage>, bool) = self.navigate_or_update(message); let task: (Task<RootMessage>, bool) = self.navigate_or_update(message);
if task.1 { if task.1 {
@@ -233,6 +249,7 @@ impl ScreenState {
} }
pub fn view(&self) -> Element<'_, RootMessage> { pub fn view(&self) -> Element<'_, RootMessage> {
println!("View update"); println!("View update");
let time = Instant::now(); let time = Instant::now();
let view = view_navigation!( let view = view_navigation!(
@@ -252,8 +269,10 @@ impl ScreenState {
Import Import
); );
let notifications = self.notification_manager.draw().map(RootMessage::Notification);
println!("View took {:?}", time.elapsed()); println!("View took {:?}", time.elapsed());
view stack![view, notifications].width(Fill).height(Fill).into()
} }
fn handle_keyboard(&mut self, message: Event) -> Task<RootMessage> { fn handle_keyboard(&mut self, message: Event) -> Task<RootMessage> {
@@ -281,7 +300,6 @@ macro_rules! view_navigation {
} }
} }
} }
#[macro_export] #[macro_export]
macro_rules! state_update { macro_rules! state_update {
($message:expr, $stack:expr, $($e:ident), *) => { ($message:expr, $stack:expr, $($e:ident), *) => {
@@ -300,7 +318,6 @@ macro_rules! state_update {
} }
} }
} }
#[macro_export] #[macro_export]
macro_rules! message_navigation { macro_rules! message_navigation {
($msg:expr, $stack:expr, $state:expr) => { ($msg:expr, $stack:expr, $state:expr) => {
@@ -321,7 +338,6 @@ macro_rules! message_navigation {
} }
}; };
} }
#[macro_export] #[macro_export]
macro_rules! state_navigate_handle { macro_rules! state_navigate_handle {
($stack:expr, $($e:ident), *) => { ($stack:expr, $($e:ident), *) => {
@@ -335,14 +351,12 @@ macro_rules! state_navigate_handle {
} }
} }
} }
pub trait NavigatedPage<T> { pub trait NavigatedPage<T> {
fn navigate(&mut self, message: &T) -> Option<Page>; fn navigate(&mut self, message: &T) -> Option<Page>;
fn navigated(&mut self); fn navigated(&mut self);
fn update(&mut self, message: T) -> Task<RootMessage>; fn update(&mut self, message: T) -> Task<RootMessage>;
fn view(&self) -> Element<'_, T>; fn view(&self) -> Element<'_, T>;
} }
pub trait KeyPressedPage { pub trait KeyPressedPage {
fn press(&mut self, message: &Event) -> Task<RootMessage>; fn press(&mut self, message: &Event) -> Task<RootMessage>;
} }
+151
View File
@@ -0,0 +1,151 @@
use crate::navigation::RootMessage;
use crate::styling::{BUTTON_RADIUS, HALF_SPACING};
use chrono::Utc;
use iced::widget::container::Style;
use iced::widget::{column, container, scrollable, text, Column};
use iced::{Element, Task};
use iced_core::alignment::Horizontal::Right;
use iced_core::alignment::Vertical;
use iced_core::Background::Color;
use iced_core::Border;
use iced_core::Length::Fill;
pub struct NotificationManager {
list: Vec<(u32, NotificationRequest, u32)>,
notification_lifetime: u32,
next_id: u32,
}
#[derive(Clone)]
pub enum NotificationMessage {
Remove(u32),
}
impl Default for NotificationManager {
fn default() -> Self {
Self::new()
}
}
impl NotificationManager {
pub fn new() -> Self {
Self {
list: Vec::new(),
notification_lifetime: 10,
next_id: 1,
}
}
pub fn draw(&self) -> Element<'_, NotificationMessage> {
let mut column = Column::new().spacing(HALF_SPACING);
for (id, notif, time) in &self.list {
column = column.push(Self::draw_notif(id, notif, time));
}
container(scrollable(column)).width(Fill).height(Fill)
.align_x(Right)
.align_y(Vertical::Top).padding(HALF_SPACING)
.into()
}
pub fn push(&mut self, notification: NotificationRequest) -> Task<RootMessage> {
let id = self.next_id;
self.list.push((
id,
notification,
Utc::now().timestamp() as u32 + self.notification_lifetime,
));
self.next_id += 1;
let message = NotificationMessage::Remove(id);
let time = self.notification_lifetime;
Task::perform(
async move {
tokio::time::sleep(core::time::Duration::from_secs(time as u64)).await;
},
|_| RootMessage::Notification(message),
)
}
pub fn update(&mut self, message: NotificationMessage) -> Task<RootMessage> {
match message {
NotificationMessage::Remove(id) => {
if let Some(index) = self.list.iter().position(|item| item.0 == id) {
self.list.remove(index);
}
}
}
Task::none()
}
fn draw_notif<'a>(
id: &u32,
notif: &NotificationRequest,
_time: &u32,
) -> Element<'a, NotificationMessage> {
let color_id;
let content = match notif {
NotificationRequest::Info(data) => {
color_id = 1;
Self::draw_data(data)
}
NotificationRequest::Warning(data) => {
color_id = 2;
Self::draw_data(data)
}
NotificationRequest::Error(data) => {
color_id = 3;
Self::draw_data(data)
}
};
let container = container(column![
iced::widget::button("✕")
.on_press(NotificationMessage::Remove(*id))
.style(iced::widget::button::text),
content
].align_x(Right).padding(HALF_SPACING));
container
.style(move |theme| {
let color = match color_id {
1 => theme.palette().success,
2 => theme.palette().warning,
3 => theme.palette().danger,
_ => theme.palette().warning,
};
Style {
text_color: None,
background: Some(Color(color.scale_alpha(0.3))),
border: Border::default().width(1).color(color).rounded(BUTTON_RADIUS),
shadow: Default::default(),
snap: false,
}
})
.into()
}
fn draw_data<'a>(data: &NotificationData) -> Element<'a, NotificationMessage> {
match data {
NotificationData::DataLoaded => {
text!("Данные успешно скачаны")
}
NotificationData::ImportError => {
text!("Ошибка при импорте данных из Anki")
}
}
.into()
}
}
#[derive(Clone)]
pub enum NotificationRequest {
Info(NotificationData),
Warning(NotificationData),
Error(NotificationData),
}
#[derive(Clone)]
pub enum NotificationData {
DataLoaded,
ImportError,
}
+2
View File
@@ -12,6 +12,7 @@ use iced::{Center, Element, Fill, Font, Length, Task};
use parking_lot::Mutex; use parking_lot::Mutex;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use crate::notification::{NotificationRequest, NotificationData};
#[derive(Clone)] #[derive(Clone)]
pub enum SyncMessage { pub enum SyncMessage {
@@ -128,6 +129,7 @@ impl NavigatedPage<SyncMessage> for SyncState {
state.decks = updated_state.decks; state.decks = updated_state.decks;
state.dictionary = updated_state.dictionary; state.dictionary = updated_state.dictionary;
state.word_groups = updated_state.word_groups; state.word_groups = updated_state.word_groups;
return Task::done(RootMessage::PushNotification(NotificationRequest::Info(NotificationData::DataLoaded)))
} }
Disable => {} Disable => {}
DisableSync => { DisableSync => {