summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock69
-rw-r--r--Cargo.toml2
-rw-r--r--requirements/kanban.md1
-rw-r--r--requirements/tasks/epic-000.md2
-rw-r--r--requirements/tasks/task-088.md2
-rw-r--r--schist_desktop_gui/Cargo.toml3
-rw-r--r--schist_desktop_gui/src/cache.rs83
-rw-r--r--schist_desktop_gui/src/dialog/new_file_dialog.rs6
-rw-r--r--schist_desktop_gui/src/dialog/pick_file_dialog.rs6
-rw-r--r--schist_desktop_gui/src/gui.rs128
-rw-r--r--schist_desktop_gui/src/gui/screens.rs2
-rw-r--r--schist_desktop_gui/src/gui/screens/files_screen.rs16
-rw-r--r--schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs123
-rw-r--r--schist_desktop_gui/src/main.rs7
14 files changed, 292 insertions, 158 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 65ced99..77f25aa 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1659,9 +1659,9 @@ dependencies = [
[[package]]
name = "hashbrown"
-version = "0.15.4"
+version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "hassle-rs"
@@ -2019,12 +2019,12 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.10.0"
+version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
+checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [
"equivalent",
- "hashbrown 0.15.4",
+ "hashbrown 0.16.1",
]
[[package]]
@@ -3342,6 +3342,9 @@ dependencies = [
"schist_models",
"schist_queries",
"schist_schema",
+ "serde",
+ "toml",
+ "unwrap-infallible",
]
[[package]]
@@ -3426,18 +3429,28 @@ checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749"
[[package]]
name = "serde"
-version = "1.0.219"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.219"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@@ -3457,11 +3470,11 @@ dependencies = [
[[package]]
name = "serde_spanned"
-version = "1.0.0"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
+checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
dependencies = [
- "serde",
+ "serde_core",
]
[[package]]
@@ -3861,14 +3874,14 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
-version = "0.9.2"
+version = "0.9.11+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac"
+checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46"
dependencies = [
"indexmap",
- "serde",
+ "serde_core",
"serde_spanned",
- "toml_datetime 0.7.0",
+ "toml_datetime 0.7.5+spec-1.1.0",
"toml_parser",
"toml_writer",
"winnow",
@@ -3882,11 +3895,11 @@ checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
[[package]]
name = "toml_datetime"
-version = "0.7.0"
+version = "0.7.5+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
+checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
dependencies = [
- "serde",
+ "serde_core",
]
[[package]]
@@ -3902,18 +3915,18 @@ dependencies = [
[[package]]
name = "toml_parser"
-version = "1.0.1"
+version = "1.0.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30"
+checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
-version = "1.0.2"
+version = "1.0.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64"
+checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
[[package]]
name = "tracing"
@@ -4042,6 +4055,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
+name = "unwrap-infallible"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e497bb1f828cc9fb236722c2eaa100dcf201563f38f4da6252357a59037adf31"
+
+[[package]]
name = "url"
version = "2.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4886,9 +4905,9 @@ dependencies = [
[[package]]
name = "winnow"
-version = "0.7.12"
+version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
+checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
dependencies = [
"memchr",
]
diff --git a/Cargo.toml b/Cargo.toml
index a763f80..66cee1d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -33,3 +33,5 @@ schist_queries = { path = "schist_core/schist_queries" }
schist_schema = { path = "schist_core/schist_schema" }
schist_traits = { path = "schist_core/schist_traits" }
serde = { version = "1.0.209", features = ["derive"] }
+toml = "0.9.11"
+unwrap-infallible = "1.0.0"
diff --git a/requirements/kanban.md b/requirements/kanban.md
index a6bcbba..6ebb1c8 100644
--- a/requirements/kanban.md
+++ b/requirements/kanban.md
@@ -4,7 +4,6 @@
| -------- | -------- | -------- |
| | | epic-000 |
| ======== | ======== | ======== |
-| | task-088 | |
| | task-085 | |
| | task-001 | |
| | task-064 | |
diff --git a/requirements/tasks/epic-000.md b/requirements/tasks/epic-000.md
index 4180a60..00e3b92 100644
--- a/requirements/tasks/epic-000.md
+++ b/requirements/tasks/epic-000.md
@@ -12,7 +12,7 @@ Status: in progress
- [x] task-012 (SQLite)
- [x] task-072 (import from Actualbudget)
- [x] task-089 (keyboard nav in files screen)
-- [ ] task-088 (caching open file)
+- [x] task-088 (caching open file)
- [ ] task-085 (view transactions)
- [ ] task-001 (inserting transactions)
- [ ] task-064 (deleting transactions)
diff --git a/requirements/tasks/task-088.md b/requirements/tasks/task-088.md
index 0d0ed59..c729b28 100644
--- a/requirements/tasks/task-088.md
+++ b/requirements/tasks/task-088.md
@@ -3,6 +3,8 @@
Schist's desktop GUI should open the same file the user last had open without
passing through the files screen.
+Status: done
+
Source: task-072
Epic: epic-000
diff --git a/schist_desktop_gui/Cargo.toml b/schist_desktop_gui/Cargo.toml
index 0ee5764..e5937d8 100644
--- a/schist_desktop_gui/Cargo.toml
+++ b/schist_desktop_gui/Cargo.toml
@@ -17,3 +17,6 @@ schist_fakes = { workspace = true }
schist_models = { workspace = true }
schist_queries = { workspace = true }
schist_schema = { workspace = true }
+serde = { workspace = true }
+toml = { workspace = true }
+unwrap-infallible = { workspace = true }
diff --git a/schist_desktop_gui/src/cache.rs b/schist_desktop_gui/src/cache.rs
new file mode 100644
index 0000000..51e2c55
--- /dev/null
+++ b/schist_desktop_gui/src/cache.rs
@@ -0,0 +1,83 @@
+use std::{
+ fs::{create_dir_all, exists, read, write, File},
+ path::{Path, PathBuf},
+};
+
+use anyhow::{Context, Result};
+use serde::{Deserialize, Serialize};
+
+const SCHIST_CACHE_DIR: &'static str = "schist";
+const SCHIST_CACHE_FILENAME: &'static str = "schist_cache.toml";
+
+#[derive(Default, Deserialize, Serialize)]
+pub struct Cache {
+ pub last_open_file: Option<String>,
+}
+
+pub struct CacheKeys {
+ pub last_open_file: &'static str,
+}
+
+pub const CACHE_KEYS: CacheKeys = CacheKeys {
+ last_open_file: "last_open_file",
+};
+
+pub fn get_cache() -> Result<Cache> {
+ const CONTEXT: &'static str = "Failed to get cache";
+ let cache_filepath = get_and_touch_cache_filepath().context(CONTEXT)?;
+ let cache_toml = read(&cache_filepath).context(CONTEXT)?;
+ let cache_table: toml::Table = toml::from_slice(&cache_toml).context(CONTEXT)?;
+ Ok(Cache {
+ last_open_file: cache_table
+ .get(&CACHE_KEYS.last_open_file.to_string())
+ .map(|o| o.as_str().map(str::to_string))
+ .flatten(),
+ })
+}
+
+pub fn put_to_cache<T>(key: &str, value: T) -> Result<()>
+where
+ toml::Value: From<T>,
+{
+ const CONTEXT: &'static str = "Failed to save cache";
+ let cache_filepath = get_and_touch_cache_filepath().context(CONTEXT)?;
+ let curr_cache_toml = read(&cache_filepath).context(CONTEXT)?;
+ let mut cache: toml::Table = toml::from_slice(&curr_cache_toml).context(CONTEXT)?;
+ cache.insert(key.to_string(), value.into());
+ let new_cache_toml = toml::to_string(&cache).context(CONTEXT)?;
+ write(cache_filepath, new_cache_toml).context(CONTEXT)?;
+ Ok(())
+}
+
+pub fn remove_from_cache(key: &str) -> Result<()> {
+ const CONTEXT: &'static str = "Failed to remove key from cache";
+ let cache_filepath = get_and_touch_cache_filepath().context(CONTEXT)?;
+ let curr_cache_toml = read(&cache_filepath).context(CONTEXT)?;
+ let mut cache: toml::Table = toml::from_slice(&curr_cache_toml).context(CONTEXT)?;
+ cache.remove(key);
+ let new_cache_toml = toml::to_string(&cache).context(CONTEXT)?;
+ write(cache_filepath, new_cache_toml).context(CONTEXT)?;
+ Ok(())
+}
+
+fn get_and_touch_cache_filepath() -> Result<PathBuf, anyhow::Error> {
+ let cache_dir = get_and_make_schist_cache_dir()?;
+ let cache_filepath = cache_dir.join(Path::new(SCHIST_CACHE_FILENAME));
+ let does_cache_file_exist = exists(cache_filepath.clone())
+ .context("Failed to determine the existence of Schist's cache file")?;
+ if !does_cache_file_exist {
+ File::create(cache_filepath.clone()).context("Failed to create a new cache file")?;
+ }
+ Ok(cache_filepath)
+}
+
+fn get_and_make_schist_cache_dir() -> anyhow::Result<PathBuf> {
+ let cache_dir = dirs::cache_dir().context("Failed to get app cache directory")?;
+ let schist_cache_dir = cache_dir.join(Path::new(SCHIST_CACHE_DIR));
+ create_dir_all(&schist_cache_dir).context(format!(
+ "Failed to create app cache directory at {}",
+ schist_cache_dir.display()
+ ))?;
+ println!("Found app cache directory at {:?}", schist_cache_dir);
+ Ok(schist_cache_dir)
+}
diff --git a/schist_desktop_gui/src/dialog/new_file_dialog.rs b/schist_desktop_gui/src/dialog/new_file_dialog.rs
index b950f68..0a148e1 100644
--- a/schist_desktop_gui/src/dialog/new_file_dialog.rs
+++ b/schist_desktop_gui/src/dialog/new_file_dialog.rs
@@ -1,6 +1,6 @@
-use crate::{database_connection::DatabaseConnectionResult, DatabaseConnection};
+use std::path::PathBuf;
-pub fn new_file_dialog<P>(starting_directory: Option<P>) -> Option<DatabaseConnectionResult>
+pub fn new_file_dialog<P>(starting_directory: Option<P>) -> Option<PathBuf>
where
P: AsRef<std::path::Path>,
{
@@ -11,5 +11,5 @@ where
if let Some(starting_directory) = starting_directory {
file_dialog = file_dialog.set_directory(starting_directory);
}
- file_dialog.save_file().map(DatabaseConnection::establish)
+ file_dialog.save_file()
}
diff --git a/schist_desktop_gui/src/dialog/pick_file_dialog.rs b/schist_desktop_gui/src/dialog/pick_file_dialog.rs
index 53d36c6..fc1a330 100644
--- a/schist_desktop_gui/src/dialog/pick_file_dialog.rs
+++ b/schist_desktop_gui/src/dialog/pick_file_dialog.rs
@@ -1,6 +1,6 @@
-use crate::{database_connection::DatabaseConnectionResult, DatabaseConnection};
+use std::path::PathBuf;
-pub fn pick_file_dialog<P>(starting_directory: Option<P>) -> Option<DatabaseConnectionResult>
+pub fn pick_file_dialog<P>(starting_directory: Option<P>) -> Option<PathBuf>
where
P: AsRef<std::path::Path>,
{
@@ -10,5 +10,5 @@ where
if let Some(starting_directory) = starting_directory {
file_dialog = file_dialog.set_directory(starting_directory);
}
- file_dialog.pick_file().map(DatabaseConnection::establish)
+ file_dialog.pick_file()
}
diff --git a/schist_desktop_gui/src/gui.rs b/schist_desktop_gui/src/gui.rs
index eb2fce9..26f147b 100644
--- a/schist_desktop_gui/src/gui.rs
+++ b/schist_desktop_gui/src/gui.rs
@@ -1,22 +1,33 @@
pub mod components;
+use actualbudget_to_schist_transformer::schist_state::{export_schist_state, SchistState};
+use itertools::Itertools;
pub mod screens;
+use std::{path::PathBuf, str::FromStr};
+
use diesel::SqliteConnection;
-use iced::Element;
+use iced::{Element, Task};
use schist_models::{Account, Bucket};
use schist_queries::{accounts::get_all_accounts, buckets::get_all_buckets};
use crate::{
+ cache::{put_to_cache, remove_from_cache, CACHE_KEYS},
config::Config,
+ dialog::{import_from_actualbudget_dialog, new_file_dialog, pick_file_dialog},
gui::screens::{files_screen, main_screen, FilesScreen, MainScreen, Screen},
- paths::get_file_paths,
+ paths::{get_file_paths, get_schist_database_directory},
shortcut::KeyBind,
traits::{Component, Viewable},
+ Cache, DatabaseConnection,
};
#[derive(Clone, Debug)]
pub enum Message {
Event(iced::Event),
+ FailedToCreateFile(String),
+ FailedToOpenFile(PathBuf, String),
+ FailedToPickFile(String),
+ FailedToImportFromActualbudget(String),
FilesScreenMessage(files_screen::Message),
MainScreenMessage(main_screen::Message),
RefetchedAccounts(FetchResult<Vec<Account>>),
@@ -47,16 +58,24 @@ pub struct Gui {
}
impl Gui {
- pub fn new() -> (Self, iced::Task<Message>) {
+ pub fn new(cache: &Cache) -> (Self, iced::Task<Message>) {
let file_paths = get_file_paths();
- let gui = Self {
+ let mut gui = Self {
active_screen: Screen::FilesScreen,
connection: None,
files_screen: FilesScreen::new(&file_paths),
main_screen: MainScreen::new(),
config: Config::default(),
};
- (gui, iced::Task::none())
+
+ let mut tasks = Vec::new();
+
+ if let Some(last_open_file) = &cache.last_open_file {
+ let Ok(last_open_file) = PathBuf::from_str(&last_open_file);
+ tasks.push(gui.open_file(&last_open_file));
+ }
+
+ (gui, Task::batch(tasks))
}
}
@@ -167,23 +186,102 @@ impl Gui {
iced::Task::none()
}
Message::FilesScreenMessage(message) => self.update_files_screen(message),
+ Message::FailedToCreateFile(err) => {
+ self.update_files_screen(files_screen::Message::ReportFailedToCreateFile(err))
+ }
+ Message::FailedToOpenFile(path, err) => {
+ self.update_files_screen(files_screen::Message::ReportFailedToOpenFile(path, err))
+ }
+ Message::FailedToImportFromActualbudget(err) => self.update_files_screen(
+ files_screen::Message::ReportFailedToImportFromActualbudget(err),
+ ),
+ Message::FailedToPickFile(err) => {
+ self.update_files_screen(files_screen::Message::ReportFailedToPickFile(err))
+ }
}
}
fn update_files_screen(&mut self, message: files_screen::Message) -> iced::Task<Message> {
match self.files_screen.update(message) {
+ files_screen::Action::ImportFromActualbudget => self.import_from_actualbudget(),
+ files_screen::Action::NewFile => self.new_file(),
files_screen::Action::None => iced::Task::none(),
- files_screen::Action::OpenedFile(sqlite_connection) => {
- let mut connection = sqlite_connection.connection;
- let buckets = get_all_buckets(&mut connection).map_err(FetchError::new);
- let accounts = get_all_accounts(&mut connection).map_err(FetchError::new);
- self.connection = Some(connection);
- self.active_screen = Screen::MainScreen;
- iced::Task::batch(vec![
- self.update(Message::RefetchedBuckets(buckets)),
- self.update(Message::RefetchedAccounts(accounts)),
- ])
+ files_screen::Action::OpenFile(path) => self.open_file(&path),
+ files_screen::Action::PickFile => self.pick_file(),
+ }
+ }
+
+ fn open_file(&mut self, file_path: &PathBuf) -> Task<Message> {
+ let sqlite_connection = DatabaseConnection::establish(file_path.to_path_buf()).ok();
+ if let Err((path, err)) = sqlite_connection {
+ return Task::done(Message::FailedToOpenFile(path, err.chain().join("\n | ")));
+ }
+ self.on_new_connection(sqlite_connection.unwrap().connection, file_path)
+ }
+
+ fn on_new_connection(
+ &mut self,
+ mut connection: SqliteConnection,
+ path: &PathBuf,
+ ) -> Task<Message> {
+ let buckets = get_all_buckets(&mut connection).map_err(FetchError::new);
+ let accounts = get_all_accounts(&mut connection).map_err(FetchError::new);
+ self.connection = Some(connection);
+ self.active_screen = Screen::MainScreen;
+ if let Some(path) = path.to_str() {
+ let _ = put_to_cache(CACHE_KEYS.last_open_file, path);
+ } else {
+ let _ = remove_from_cache(CACHE_KEYS.last_open_file);
+ }
+ iced::Task::batch(vec![
+ self.update(Message::RefetchedBuckets(buckets)),
+ self.update(Message::RefetchedAccounts(accounts)),
+ ])
+ }
+
+ fn new_file(&mut self) -> Task<Message> {
+ match new_file_dialog(get_schist_database_directory().ok()) {
+ Some(path) => self.open_file(&path),
+ None => Task::none(),
+ }
+ }
+
+ fn new_file_with_state(&mut self, state: &SchistState) -> Task<Message> {
+ match new_file_dialog(get_schist_database_directory().ok()) {
+ Some(path) => {
+ let sqlite_connection = DatabaseConnection::establish(path.clone()).ok();
+ if let Err((path, err)) = sqlite_connection {
+ return Task::done(Message::FailedToOpenFile(path, err.chain().join("\n | ")));
+ }
+ let mut db_conn = sqlite_connection.unwrap().connection;
+ export_schist_state(state, &mut db_conn).map_or_else(
+ |err| Task::done(Message::FailedToCreateFile(err.chain().join("\n | "))),
+ move |()| self.on_new_connection(db_conn, &path),
+ )
}
+ None => Task::none(),
+ }
+ }
+
+ fn import_from_actualbudget(&mut self) -> Task<Message> {
+ match import_from_actualbudget_dialog() {
+ Some(Ok(state)) => self.new_file_with_state(&state),
+ Some(Err((_path, err))) => Task::done(Message::FailedToImportFromActualbudget(
+ err.chain().join("\n | "),
+ )),
+ None => Task::none(),
+ }
+ }
+
+ fn pick_file(&mut self) -> Task<Message> {
+ match pick_file_dialog(get_schist_database_directory().ok()) {
+ Some(path) => match DatabaseConnection::establish(path.clone()).ok() {
+ Ok(db_conn) => self.on_new_connection(db_conn.connection, &path),
+ Err((_path, err)) => {
+ Task::done(Message::FailedToPickFile(err.chain().join("\n | ")))
+ }
+ },
+ None => Task::none(),
}
}
}
diff --git a/schist_desktop_gui/src/gui/screens.rs b/schist_desktop_gui/src/gui/screens.rs
index abca2b6..5acf73b 100644
--- a/schist_desktop_gui/src/gui/screens.rs
+++ b/schist_desktop_gui/src/gui/screens.rs
@@ -4,7 +4,7 @@ pub mod main_screen;
pub use files_screen::FilesScreen;
pub use main_screen::MainScreen;
-#[derive(Default)]
+#[derive(Clone, Default)]
pub enum Screen {
#[default]
FilesScreen,
diff --git a/schist_desktop_gui/src/gui/screens/files_screen.rs b/schist_desktop_gui/src/gui/screens/files_screen.rs
index 9757355..9700e7a 100644
--- a/schist_desktop_gui/src/gui/screens/files_screen.rs
+++ b/schist_desktop_gui/src/gui/screens/files_screen.rs
@@ -3,13 +3,10 @@ mod view_files_screen;
use std::{fmt::Debug, path::PathBuf};
-use crate::{
- gui::components::{navigation, Navigation, Panel},
- DatabaseConnection,
-};
+use crate::gui::components::{navigation, Navigation, Panel};
pub struct FilesScreen {
- options: Navigation<Panel<OptionId>>,
+ pub options: Navigation<Panel<OptionId>>,
}
#[derive(Clone, Debug, PartialEq)]
@@ -23,6 +20,10 @@ pub enum OptionId {
#[derive(Clone, Debug, PartialEq)]
pub enum Message {
NavigationMessage(navigation::Message<Panel<OptionId>>),
+ ReportFailedToCreateFile(String),
+ ReportFailedToImportFromActualbudget(String),
+ ReportFailedToPickFile(String),
+ ReportFailedToOpenFile(PathBuf, String),
ActivateSelectedOption,
NextOption,
PrevOption,
@@ -30,8 +31,11 @@ pub enum Message {
#[derive(Debug)]
pub enum Action {
+ ImportFromActualbudget,
+ NewFile,
None,
- OpenedFile(DatabaseConnection),
+ OpenFile(PathBuf),
+ PickFile,
}
impl<'a> FilesScreen {
diff --git a/schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs b/schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs
index aa47f56..1339138 100644
--- a/schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs
+++ b/schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs
@@ -1,13 +1,6 @@
-use actualbudget_to_schist_transformer::schist_state::{export_schist_state, SchistState};
-use itertools::Itertools;
-
use crate::{
- database_connection::DatabaseConnectionResult,
- dialog::{import_from_actualbudget_dialog, new_file_dialog, pick_file_dialog},
gui::components::{navigation, Panel},
- paths::get_schist_database_directory,
traits::Component,
- DatabaseConnection,
};
use super::{Action, FilesScreen, Message, OptionId};
@@ -24,92 +17,31 @@ impl<'a> Component<'a, Message, Action> for FilesScreen {
}
None => Action::None,
},
- }
- }
-}
-
-impl FilesScreen {
- fn pick_file(
- &mut self,
- ) -> Option<Result<DatabaseConnection, (std::path::PathBuf, anyhow::Error)>> {
- pick_file_dialog(get_schist_database_directory().ok()).map(|db_connection_result| {
- db_connection_result.ok().inspect_err(|(_path, err)| {
- self.report_err(
- super::OptionId::PickFile,
- format!("{}", err.chain().join("\n | ")).as_str(),
- "Failed to open file",
- );
- })
- })
- }
-
- fn open_file(
- &mut self,
- file_path: std::path::PathBuf,
- ) -> Result<DatabaseConnection, (std::path::PathBuf, anyhow::Error)> {
- DatabaseConnection::establish(file_path.clone())
- .ok()
- .inspect_err(|(_path, err)| {
- self.report_err(
- OptionId::OpenFile(file_path),
- format!("{}", err.chain().join("\n | ")).as_str(),
- "Failed to open file",
- );
- })
- }
-
- fn new_file(
- &mut self,
- ) -> Option<Result<DatabaseConnection, (std::path::PathBuf, anyhow::Error)>> {
- new_file_dialog(get_schist_database_directory().ok()).map(|result| {
- result.ok().inspect_err(|(_path, err)| {
- self.report_err(
- OptionId::NewFile,
- "Failed to create new file",
- format!("{}", err.chain().join("\n | ")).as_str(),
- );
- })
- })
- }
-
- fn import_from_actualbudget(&mut self) -> Option<anyhow::Result<DatabaseConnection>> {
- match import_from_actualbudget_dialog() {
- Some(Ok(state)) => self.new_file_with_state(&state),
- Some(Err((_path, err))) => Some({
+ Message::ReportFailedToOpenFile(path, err) => {
+ self.report_err(OptionId::OpenFile(path), &err, "Failed to open file");
+ Action::None
+ }
+ Message::ReportFailedToCreateFile(err) => {
+ self.report_err(OptionId::NewFile, &err, "Failed to create new file");
+ Action::None
+ }
+ Message::ReportFailedToImportFromActualbudget(err) => {
self.report_err(
OptionId::ImportFromActualbudget,
+ &err,
"Failed to import from Actualbudget",
- format!("{}", err.chain().join("\n | ")).as_str(),
- );
- Err(err)
- }),
- None => None,
- }
- }
-
- fn new_file_with_state(
- &mut self,
- state: &SchistState,
- ) -> Option<anyhow::Result<DatabaseConnection>> {
- match new_file_dialog(get_schist_database_directory().ok())
- .map(DatabaseConnectionResult::ok)
- {
- Some(Ok(mut file)) => Some({
- export_schist_state(state, &mut file.connection).ok()?;
- Ok(file)
- }),
- Some(Err((_path, err))) => Some({
- self.report_err(
- OptionId::NewFile,
- format!("{}", err.chain().join("\n | ")).as_str(),
- "Failed to create new file",
);
- Err(err)
- }),
- None => None,
+ Action::None
+ }
+ Message::ReportFailedToPickFile(err) => {
+ self.report_err(OptionId::PickFile, &err, "Failed to choose file");
+ Action::None
+ }
}
}
+}
+impl FilesScreen {
fn report_err(&mut self, id: OptionId, err: &str, dialog_title: &str) {
let old_panel = self.options.find(|o| o.id == id);
let file_panel_with_err = Panel::new(
@@ -134,33 +66,22 @@ impl FilesScreen {
Panel {
id: OptionId::NewFile,
..
- } => match self.new_file() {
- Some(Ok(file)) => Action::OpenedFile(file),
- Some(Err(_)) | None => Action::None,
- },
+ } => Action::NewFile,
Panel {
id: OptionId::OpenFile(file_path),
..
- } => self
- .open_file(file_path.clone())
- .map_or(Action::None, Action::OpenedFile),
+ } => Action::OpenFile(file_path),
Panel {
id: OptionId::PickFile,
..
- } => match self.pick_file() {
- Some(Ok(file)) => Action::OpenedFile(file),
- Some(Err(_)) | None => Action::None,
- },
+ } => Action::PickFile,
Panel {
id: OptionId::ImportFromActualbudget,
..
- } => match self.import_from_actualbudget() {
- Some(Ok(file)) => Action::OpenedFile(file),
- Some(Err(_)) | None => Action::None,
- },
+ } => Action::ImportFromActualbudget,
},
navigation::Action::SelectOption(_) | navigation::Action::None => Action::None,
}
diff --git a/schist_desktop_gui/src/main.rs b/schist_desktop_gui/src/main.rs
index 6dd2622..a5749d3 100644
--- a/schist_desktop_gui/src/main.rs
+++ b/schist_desktop_gui/src/main.rs
@@ -1,3 +1,4 @@
+mod cache;
mod config;
mod database_connection;
mod dialog;
@@ -10,22 +11,24 @@ mod theme;
mod traits;
mod window_settings;
+pub use cache::Cache;
pub use database_connection::DatabaseConnection;
use anyhow::Context;
use crate::{
- config::Config, gui::Gui, settings::make_settings, theme::make_theme,
+ cache::get_cache, config::Config, gui::Gui, settings::make_settings, theme::make_theme,
window_settings::make_window_settings,
};
fn main() -> anyhow::Result<()> {
+ let cache = get_cache().unwrap_or_else(|_| Cache::default());
let config = Config::default();
iced::application("Schist", Gui::update, Gui::view)
.settings(make_settings(&config.clone()))
.subscription(gui::subscription)
.theme(make_theme)
.window(make_window_settings(&config.clone()))
- .run_with(Gui::new)
+ .run_with(move || Gui::new(&cache))
.context("Failed to run Schist GUI")
}