summaryrefslogtreecommitdiff
path: root/schist_desktop_gui/src/gui/screens/files_screen
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/files_screen
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/files_screen')
-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
2 files changed, 231 insertions, 0 deletions
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()
+}