summaryrefslogtreecommitdiff
path: root/schist_desktop_gui/src/gui/screens
diff options
context:
space:
mode:
authorJoe Carstairs <me@joeac.net>2025-08-25 16:35:45 +0000
committerjoeac <me@joeac.net>2025-08-25 16:35:45 +0000
commitfe85652e4d3e8906824baddaafd05d19105579b8 (patch)
tree31b56e31cbf289a34ec1d6707becf3c60933b56f /schist_desktop_gui/src/gui/screens
parentd082a30de6a0cbd1fbe2ab5a4316db0891004129 (diff)
task-072
Co-authored-by: Joe Carstairs <jcarstairs@scottlogic.com> Reviewed-on: https://git.joeac.net/joeac/schist/pulls/3 Co-authored-by: Joe Carstairs <me@joeac.net> Co-committed-by: Joe Carstairs <me@joeac.net>
Diffstat (limited to 'schist_desktop_gui/src/gui/screens')
-rw-r--r--schist_desktop_gui/src/gui/screens/files_screen.rs36
-rw-r--r--schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs125
-rw-r--r--schist_desktop_gui/src/gui/screens/files_screen/view_files_screen.rs106
-rw-r--r--schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs10
-rw-r--r--schist_desktop_gui/src/gui/screens/main_screen/view.rs4
5 files changed, 274 insertions, 7 deletions
diff --git a/schist_desktop_gui/src/gui/screens/files_screen.rs b/schist_desktop_gui/src/gui/screens/files_screen.rs
new file mode 100644
index 0000000..68a9556
--- /dev/null
+++ b/schist_desktop_gui/src/gui/screens/files_screen.rs
@@ -0,0 +1,36 @@
+mod update_files_screen;
+mod view_files_screen;
+
+use std::{fmt::Debug, path::PathBuf};
+
+use crate::DatabaseConnection;
+
+pub struct FilesScreen {
+ new_file_err: Option<(PathBuf, String)>,
+ file_paths: Vec<PathBuf>,
+ open_file_err: Option<(PathBuf, String)>,
+}
+
+#[derive(Clone, Debug)]
+pub enum Message {
+ ImportFromActualbudget,
+ NewFile,
+ OpenFile(std::path::PathBuf),
+ PickFile,
+}
+
+#[derive(Debug)]
+pub enum Action {
+ None,
+ OpenedFile(DatabaseConnection),
+}
+
+impl<'a> FilesScreen {
+ pub fn new(file_paths: &'a [PathBuf]) -> Self {
+ Self {
+ file_paths: file_paths.to_vec(),
+ new_file_err: None,
+ open_file_err: None,
+ }
+ }
+}
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
new file mode 100644
index 0000000..9c45d6e
--- /dev/null
+++ b/schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs
@@ -0,0 +1,125 @@
+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},
+ paths::get_schist_database_directory,
+ traits::Component,
+ DatabaseConnection,
+};
+
+use super::{Action, FilesScreen, Message};
+
+impl<'a> Component<'a, Message, Action> for FilesScreen {
+ fn update(&mut self, message: Message) -> Action {
+ match message {
+ Message::NewFile => match self.new_file() {
+ Some(Ok(file)) => Action::OpenedFile(file),
+ Some(Err(_)) | None => Action::None,
+ },
+
+ Message::OpenFile(file_path) => self
+ .open_file(file_path)
+ .map_or(Action::None, Action::OpenedFile),
+
+ Message::PickFile => match self.pick_file() {
+ Some(Ok(file)) => Action::OpenedFile(file),
+ Some(Err(_)) | None => Action::None,
+ },
+
+ Message::ImportFromActualbudget => match self.import_from_actualbudget() {
+ Some(Ok(file)) => Action::OpenedFile(file),
+ Some(Err(_)) | 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.open_file_err = Some((path.clone(), err.to_string()));
+ rfd::MessageDialog::new()
+ .set_title("Failed to import from Actualbudget")
+ .set_level(rfd::MessageLevel::Error)
+ .set_description(format!("{}", err.chain().join("\n | ")))
+ .show();
+ })
+ })
+ }
+
+ 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.open_file_err = Some((file_path, err.to_string()));
+ rfd::MessageDialog::new()
+ .set_title("Failed to import from Actualbudget")
+ .set_level(rfd::MessageLevel::Error)
+ .set_description(format!("{}", err.chain().join("\n | ")))
+ .show();
+ })
+ }
+
+ 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.new_file_err = Some((path.clone(), err.to_string()));
+ rfd::MessageDialog::new()
+ .set_title("Failed to import from Actualbudget")
+ .set_level(rfd::MessageLevel::Error)
+ .set_description(format!("{}", err.chain().join("\n | ")))
+ .show();
+ })
+ })
+ }
+
+ 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({
+ self.open_file_err = Some((path, err.to_string()));
+ rfd::MessageDialog::new()
+ .set_title("Failed to import from Actualbudget")
+ .set_level(rfd::MessageLevel::Error)
+ .set_description(format!("{}", err.chain().join("\n | ")))
+ .show();
+ 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.new_file_err = Some((path, err.to_string()));
+ rfd::MessageDialog::new()
+ .set_title("Failed to import from Actualbudget")
+ .set_level(rfd::MessageLevel::Error)
+ .set_description(format!("{}", err.chain().join("\n | ")))
+ .show();
+ Err(err)
+ }),
+ None => None,
+ }
+ }
+}
diff --git a/schist_desktop_gui/src/gui/screens/files_screen/view_files_screen.rs b/schist_desktop_gui/src/gui/screens/files_screen/view_files_screen.rs
new file mode 100644
index 0000000..3cbd939
--- /dev/null
+++ b/schist_desktop_gui/src/gui/screens/files_screen/view_files_screen.rs
@@ -0,0 +1,106 @@
+use std::path::PathBuf;
+
+use iced::widget::column;
+
+use crate::{
+ gui::components::{panel_button, Text},
+ style::*,
+ traits::Viewable,
+};
+
+use super::{FilesScreen, Message};
+
+impl<'a> Viewable<'a, Message> for FilesScreen {
+ fn view(&'a self) -> iced::Element<'a, Message> {
+ let mut cols = Vec::new();
+ if let Some(col) = self.view_new_file_err() {
+ cols.push(col);
+ };
+ if let Some(col) = self.view_open_file_err() {
+ cols.push(col);
+ };
+ cols.push(self.view_new_file_button());
+ cols.push(self.view_open_file_button());
+ cols.push(self.view_import_from_actualbudget_button());
+ cols.append(&mut self.view_files());
+ column(cols).padding(SPACING_LG).spacing(SPACING_MD).into()
+ }
+}
+
+impl<'a> FilesScreen {
+ fn view_new_file_err(&'a self) -> Option<iced::Element<'a, Message>> {
+ self.new_file_err.as_ref().map(|(path, err)| {
+ let msg = format!("Failed to open new file {:#?}: {:#?}", path, err);
+ Text::new(&msg).danger().into()
+ })
+ }
+
+ fn view_open_file_err(&'a self) -> Option<iced::Element<'a, Message>> {
+ self.open_file_err
+ .as_ref()
+ .filter(|(file_path, _err)| !self.file_paths.iter().any(|fp| *fp == *file_path))
+ .map(|(file_path, err)| {
+ let msg = format!("Failed to open new file {:#?}: {:#?}", file_path, err);
+ Text::new(&msg).danger().into()
+ })
+ }
+
+ fn view_files(&'a self) -> Vec<iced::Element<'a, Message>> {
+ self.file_paths
+ .iter()
+ .map(|f| {
+ view_file(
+ f,
+ self.open_file_err
+ .clone()
+ .filter(|(file, _err)| *file == *f)
+ .map(|(_file, err)| err),
+ )
+ })
+ .collect()
+ }
+
+ fn view_new_file_button(&'a self) -> iced::Element<'a, Message> {
+ panel_button::<Text, Message>(Text::default("New file").into(), Message::NewFile).into()
+ }
+
+ fn view_open_file_button(&'a self) -> iced::Element<'a, Message> {
+ panel_button::<Text, Message>(Text::default("Open file").into(), Message::PickFile).into()
+ }
+
+ fn view_import_from_actualbudget_button(&'a self) -> iced::Element<'a, Message> {
+ panel_button::<Text, Message>(
+ Text::default("Import from Actualbudget").into(),
+ Message::ImportFromActualbudget,
+ )
+ .into()
+ }
+}
+
+fn view_file<'a>(file_path: &'a PathBuf, err: Option<String>) -> iced::Element<'a, Message> {
+ let mut cols: Vec<iced::Element<'a, Message>> = err
+ .map(|err| {
+ let msg = format!("Failed to open new file {:#?}: {:#?}", file_path, err);
+ vec![Text::new(&msg).danger().small().into()]
+ })
+ .unwrap_or_else(Vec::new);
+
+ cols.push(
+ Text::default(file_path.to_str().unwrap_or(""))
+ .small()
+ .weak()
+ .into(),
+ );
+
+ cols.push(
+ Text::default(
+ file_path
+ .file_name()
+ .map(|os_str| os_str.to_str().unwrap_or("[invalid Unicode]"))
+ .unwrap_or("[invalid filename]"),
+ )
+ .into(),
+ );
+
+ panel_button(column(cols), Message::OpenFile(file_path.clone())).into()
+}
diff --git a/schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs b/schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs
index 0af3c28..00f4ec3 100644
--- a/schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs
+++ b/schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs
@@ -147,12 +147,12 @@ impl<'a> Component<'a, Message, Action> for MainScreen {
}
impl MainScreen {
- pub fn new(buckets: Vec<Bucket>, accounts: Vec<Account>) -> Self {
+ pub fn new() -> Self {
Self {
- balances_view: BalancesView::new(buckets.clone()),
- buckets: buckets.clone(),
- buckets_view: BucketsView::new(buckets, "Hello, buckets!"),
- transactions_view: TransactionsView::new(accounts, "Hello, transactions!"),
+ balances_view: BalancesView::new(Vec::new()),
+ buckets: Vec::new(),
+ buckets_view: BucketsView::new(Vec::new(), "Hello, buckets!"),
+ transactions_view: TransactionsView::new(Vec::new(), "Hello, transactions!"),
active_view: View::Balances,
view_navigation: Navigation::new(Some(View::Balances), View::views()),
}
diff --git a/schist_desktop_gui/src/gui/screens/main_screen/view.rs b/schist_desktop_gui/src/gui/screens/main_screen/view.rs
index eb9c88e..2be427b 100644
--- a/schist_desktop_gui/src/gui/screens/main_screen/view.rs
+++ b/schist_desktop_gui/src/gui/screens/main_screen/view.rs
@@ -1,4 +1,4 @@
-use iced::Element;
+use iced::{widget::Text, Element};
use crate::gui::components::navigation;
@@ -43,6 +43,6 @@ impl View {
impl<'a> Into<Element<'a, navigation::Message<View>>> for View {
fn into(self) -> Element<'a, navigation::Message<View>> {
- iced::widget::text(self.name()).into()
+ Text::new(self.name()).into()
}
}