use crate::data_provider::settings::{delete_settings, set_setting}; use crate::data_provider::web_api::*; use crate::navigation::Page::PreviousPage; use crate::navigation::{NavigatedPage, Page}; use crate::styling::*; use crate::sync::SyncMessage::*; use crate::{fill_state, AppState, RootMessage}; use iced::widget::button::danger; use iced::widget::container::rounded_box; use iced::widget::{button, column, container, progress_bar, row, space, text, toggler}; use iced::{Center, Element, Fill, Font, Length, Task}; use std::sync::{Arc, Mutex}; use std::time::Duration; #[derive(Clone)] pub enum SyncMessage { Back, CopyKey, KeyCopied, IdReceived(String), InitSync, GetKey, Send, GetLast, NextAnimation, NetworkFinished, Disable, DisableSync, SwitchAutoSync, } #[derive(Clone)] pub struct SyncState { state: Arc>, auto_fetch: bool, frozen: bool, progress: f32, } impl NavigatedPage for SyncState { fn navigate(&self, message: &SyncMessage) -> Option { if let Back = message && !self.frozen { Some(PreviousPage) } else { None } } fn navigated(&mut self) {} } impl SyncState { pub fn new(state: Arc>) -> SyncState { let auto_fetch; { auto_fetch = state.lock().unwrap().sync_data.auto_web_fetch; } Self { auto_fetch, state, frozen: false, progress: 0.0, } } pub fn update(&mut self, message: SyncMessage) -> Task { match message { Back => {} CopyKey => { let state = self.state.lock().unwrap(); let key = state.sync_data.key.clone().unwrap(); return iced::clipboard::write(key) .map(|_val: String| RootMessage::Sync(KeyCopied)); } InitSync => { return Task::perform(first_sync(), |id| RootMessage::Sync(IdReceived(id))); } GetKey => { return iced::clipboard::read() .map(|key| RootMessage::Sync(IdReceived(key.unwrap_or_else(String::new)))); } KeyCopied => {} IdReceived(new_id) => { if validate_id(&new_id) == false { return Task::none(); } let mut state = self.state.lock().unwrap(); state.sync_data.key = Some(new_id.clone()); set_setting("SYNC_KEY".to_string(), new_id, &state.connection); } Send => { self.prepare_to_db_interaction(); let id = self.state.lock().unwrap().sync_data.key.clone().unwrap(); let tasks = Task::batch([ Task::perform( async { tokio::time::sleep(Duration::from_millis(200)).await }, |_| RootMessage::Sync(NextAnimation), ), Task::perform(send_data(id), |_| RootMessage::Sync(NetworkFinished)), ]); return tasks; } GetLast => { let id = self.state.lock().unwrap().sync_data.key.clone().unwrap(); let tasks = Task::batch([ Task::perform( async { tokio::time::sleep(Duration::from_millis(200)).await }, |_| RootMessage::Sync(NextAnimation), ), Task::perform(load_data(id, false), |_| RootMessage::Sync(NetworkFinished)), ]); return tasks; } NextAnimation => { self.progress += 1.0; if self.progress > 5.0 { self.progress = 0.0; } if self.frozen { return Task::perform( async { tokio::time::sleep(Duration::from_millis(200)).await }, |_| RootMessage::Sync(NextAnimation), ); } } NetworkFinished => { self.frozen = false; let mut updated_state = AppState::new(); fill_state(&mut updated_state); let mut state = self.state.lock().unwrap(); state.sync_data = updated_state.sync_data; state.connection = updated_state.connection; state.card_sets = updated_state.card_sets; state.dictionary = updated_state.dictionary; state.word_groups = updated_state.word_groups; } Disable => {} DisableSync => { let mut state = self.state.lock().unwrap(); state.sync_data.key = None; delete_settings("SYNC_KEY".to_string(), &state.connection); } SwitchAutoSync => { self.auto_fetch = !self.auto_fetch; let mut state = self.state.lock().unwrap(); state.sync_data.auto_web_fetch = self.auto_fetch; set_setting("AUTO_WEB_FETCH".to_string(), self.auto_fetch.to_string(), &state.connection); } } Task::none() } pub fn view(&self) -> Element<'_, SyncMessage> { back_overlay( row![space().width(Fill), self.sync_column(), space().width(Fill)] .spacing(DEFAULT_SPACING) .width(Fill) .into(), Back, ) } fn sync_column(&self) -> Element<'_, SyncMessage> { let network_view: Element<'_, SyncMessage> = if self.frozen { progress_bar(0.0..=5.0, self.progress).into() } else { space().into() }; { if let Some(key) = self.state.lock().unwrap().sync_data.key.clone() { column![ text!("Ваш ключ синхронизации"), container( container( text!("{}", key) .size(ACCENT_FONT_SIZE) .font(Font::MONOSPACE) ) .padding(3) ) .style(rounded_box), button("Скопировать в буфер обмена") .style(jl_button) .on_press(CopyKey) .width(Fill), row![ button("↑ Отправить").style(jl_button).on_press(Send), space().width(Fill), button("↓ Скачать").style(jl_button).on_press(GetLast) ] .spacing(DEFAULT_SPACING), toggler(self.auto_fetch).label("Автоматическая синхронизация").on_toggle(|_| SwitchAutoSync), container(network_view).width(Fill), button("Отключить синхронизацию") .style(danger) .on_press(DisableSync), ] } else { column![ button("Создать сохранение") .style(jl_button) .on_press(InitSync), button("Вставить ключ из буфера") .style(jl_button) .on_press(GetKey), ] } } .spacing(DEFAULT_SPACING) .align_x(Center) .width(Length::Shrink) .into() } /* fn app_updater(&self) -> Element<'_, SyncMessage> { column![].width(Fill).into() }*/ fn prepare_to_db_interaction(&mut self) { if self.frozen { return; } self.frozen = true; self.progress = 0.0; let state = self.state.lock().unwrap(); state .connection .query_row("PRAGMA wal_checkpoint(TRUNCATE)", [], |_| Ok(())) .unwrap_or_else(|e| { eprintln!("Ошибка при checkpoint WAL: {e}"); }); } } #[inline] fn validate_id(id: &str) -> bool { id.len() == 24 && id.chars().all(|c| c.is_ascii_alphanumeric()) }