From 40a2e727f8ac8d1c74fda3e38d9d27533c5cad3a Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Wed, 23 Jul 2025 03:00:49 -0300 Subject: [PATCH 01/12] Add the modals to delete the diary and the diary contents --- .../delete_all_entries_from_diary_modal.py | 20 +++++++ .../delete_all_photos_from_diary_modal.py | 20 +++++++ .../ui/screens/modals/delete_diary_modal.py | 59 +++++++++++++++++++ .../modals/delete_yes_confirmation_modal.py | 54 +++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 src/pilgrim/ui/screens/modals/delete_all_entries_from_diary_modal.py create mode 100644 src/pilgrim/ui/screens/modals/delete_all_photos_from_diary_modal.py create mode 100644 src/pilgrim/ui/screens/modals/delete_diary_modal.py create mode 100644 src/pilgrim/ui/screens/modals/delete_yes_confirmation_modal.py diff --git a/src/pilgrim/ui/screens/modals/delete_all_entries_from_diary_modal.py b/src/pilgrim/ui/screens/modals/delete_all_entries_from_diary_modal.py new file mode 100644 index 0000000..ec5d5ad --- /dev/null +++ b/src/pilgrim/ui/screens/modals/delete_all_entries_from_diary_modal.py @@ -0,0 +1,20 @@ + +from textual.widgets import Button + +from textual import on + +from pilgrim.ui.screens.modals.delete_yes_confirmation_modal import DeleteYesConfirmationModal + + +class DeleteAllEntriesModal(DeleteYesConfirmationModal): + def __init__(self,diary_id:int): + super().__init__(diary_id) + self.head_text.update("Are you sure you want to delete all entries from this diary?") + + + + @on(Button.Pressed, "#DeleteDiaryModal-DeleteButton") + def on_delete_button_pressed(self, event): + self.result = True + self.dismiss() + diff --git a/src/pilgrim/ui/screens/modals/delete_all_photos_from_diary_modal.py b/src/pilgrim/ui/screens/modals/delete_all_photos_from_diary_modal.py new file mode 100644 index 0000000..d34b8bb --- /dev/null +++ b/src/pilgrim/ui/screens/modals/delete_all_photos_from_diary_modal.py @@ -0,0 +1,20 @@ + +from textual.widgets import Button + +from textual import on + +from pilgrim.ui.screens.modals.delete_yes_confirmation_modal import DeleteYesConfirmationModal + + +class DeleteAllPhotosModal(DeleteYesConfirmationModal): + def __init__(self,diary_id:int): + super().__init__(diary_id) + self.head_text.update("Are you sure you want to delete all photos from this diary?") + + + + @on(Button.Pressed, "#DeleteDiaryModal-DeleteButton") + def on_delete_button_pressed(self, event): + self.result = True + self.dismiss() + diff --git a/src/pilgrim/ui/screens/modals/delete_diary_modal.py b/src/pilgrim/ui/screens/modals/delete_diary_modal.py new file mode 100644 index 0000000..61e6597 --- /dev/null +++ b/src/pilgrim/ui/screens/modals/delete_diary_modal.py @@ -0,0 +1,59 @@ +from textual.containers import Container +from textual.widgets import Header, Footer, Label, Button,Input +from textual.screen import Screen +from textual.binding import Binding +from textual import on + + +class DeleteDiaryModal(Screen): + + BINDINGS = [ + Binding("escape","cancel","Cancel"), + ] + def __init__(self, diary_id: int,diary_name:str): + super().__init__() + self.diary_id = diary_id + self.diary_name = diary_name + self.user_input = Input(placeholder=f"Type diary name to confirm: ({self.diary_name})",id="DeleteDiaryModal-UserInput") + self.delete_button = Button("Delete Diary",id="DeleteDiaryModal-DeleteButton",disabled=True) + self.cancel_button = Button("Cancel",id="DeleteDiaryModal-CancelButton") + self.result = None + + def compose(self): + yield Header() + yield Container( + Label("Are you sure you want to delete this diary?"), + self.user_input, + Container( + self.delete_button, + self.cancel_button, + id="DeleteDiaryModal-ButtonContainer", + classes="DeleteDiaryModal-ButtonContainer" + ), + id="DeleteDiaryModal-MainContainer", + classes="DeleteDiaryModal-MainContainer" + ) + yield Footer() + + @on(Input.Changed,"#DeleteDiaryModal-UserInput") + def on_user_input_changed(self, event): + input_text = event.value.strip() + + if input_text == self.diary_name: + self.delete_button.disabled = False + else: + self.delete_button.disabled = True + + @on(Button.Pressed,"#DeleteDiaryModal-DeleteButton") + def on_delete_button_pressed(self, event): + self.result = True + self.dismiss() + + + @on(Button.Pressed,"#DeleteDiaryModal-CancelButton") + def on_cancel_button_pressed(self, event): + self.action_cancel() + + + def action_cancel(self): + self.dismiss() diff --git a/src/pilgrim/ui/screens/modals/delete_yes_confirmation_modal.py b/src/pilgrim/ui/screens/modals/delete_yes_confirmation_modal.py new file mode 100644 index 0000000..8c6c9bf --- /dev/null +++ b/src/pilgrim/ui/screens/modals/delete_yes_confirmation_modal.py @@ -0,0 +1,54 @@ +from textual.containers import Container +from textual.widgets import Header, Footer, Label, Button,Input +from textual.screen import Screen +from textual.binding import Binding +from textual import on + + +class DeleteYesConfirmationModal(Screen): + BINDINGS = [ + Binding("escape", "cancel", "Cancel"), + ] + def __init__(self,diary_id:int): + super().__init__() + self.diary_id = diary_id + self.user_input = Input(placeholder="Type 'Yes, I do ' to confirm",id="DeleteYesConfirmationModal-UserInput") + self.delete_button = Button("Delete",id="DeleteYesConfirmationModal-DeleteButton",disabled=True) + self.cancel_button = Button("Cancel",id="DeleteYesConfirmationModal-CancelButton") + self.head_text = Label("Are you sure you want to delete this diary?",id="DeleteYesConfirmationModal-HeadText") + self.second_head_text = Label("This action cannot be undone.",id="DeleteYesConfirmationModal-SecondHeadText") + self.delete_modal_container = Container( + self.head_text, + self.second_head_text, + self.user_input, + Container( + self.delete_button, + self.cancel_button, + id="DeleteYesConfirmationModal-DeleteButtonContainer", + classes="DeleteYesConfirmationModal-DeleteButtonContainer" + ), + id="DeleteYesConfirmationModal-DeleteModalContainer", + classes="DeleteYesConfirmationModal-DeleteModalContainer" + ) + self.result = None + + @on(Input.Changed,"#DeleteYesConfirmationModal-UserInput") + def on_user_input_changed(self, event): + input_text = event.value.strip() + if input_text == "Yes, I do": + self.delete_button.disabled = False + else: + self.delete_button.disabled = True + + @on(Button.Pressed,"#DeleteYesConfirmationModal-CancelButton") + def on_cancel_button_pressed(self, event): + self.action_cancel() + + def action_cancel(self): + self.dismiss() + self.app.push_screen(SettingsScreen(diary=travel_diary[0])) + + def compose(self): + yield Header() + yield Footer() + yield self.delete_modal_container \ No newline at end of file From d1afd3a7ef1621b63eb6a533f080ed11a0ee0b0f Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Wed, 23 Jul 2025 04:22:51 -0300 Subject: [PATCH 02/12] Add the css for the new modals --- src/pilgrim/ui/styles/pilgrim.css | 81 ++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/src/pilgrim/ui/styles/pilgrim.css b/src/pilgrim/ui/styles/pilgrim.css index 5b9bf3b..ce93076 100644 --- a/src/pilgrim/ui/styles/pilgrim.css +++ b/src/pilgrim/ui/styles/pilgrim.css @@ -623,4 +623,83 @@ Screen.-modal { .ConfirmDeleteModal-Button { margin: 0 1; width: 1fr; -} \ No newline at end of file +} + +.DeleteYesConfirmationModal-DeleteModalContainer, +.DeleteDiaryModal-MainContainer { + align: center middle; + layout: vertical; + margin: 2; + padding: 2; + background: $primary-background; + height: auto; + width: auto; + min-width: 80%; + max-width: 95%; + border: solid $primary; +} + +/* Labels de texto */ +.DeleteYesConfirmationModal-DeleteModalContainer > Label, +.DeleteDiaryModal-MainContainer > Label { + margin: 1 0; + padding: 0 1; + color: $error; + text-align: center; + width: 100%; +} + +/* Input fields */ +.DeleteYesConfirmationModal-DeleteModalContainer > Input, +.DeleteDiaryModal-MainContainer > Input { + margin: 1 0; + width: 100%; + height: 3; +} + +/* Container dos botões */ +.DeleteYesConfirmationModal-DeleteButtonContainer, +.DeleteDiaryModal-ButtonContainer { + layout: horizontal; + align: center middle; + margin: 2 0 0 0; + height: auto; + width: 100%; + padding: 0; +} + +/* Botões individuais */ +.DeleteYesConfirmationModal-DeleteButtonContainer > Button, +.DeleteDiaryModal-ButtonContainer > Button { + width: 45%; + margin: 0 1; + height: 3; +} + +/* Botão de delete (primeiro botão) */ +.DeleteYesConfirmationModal-DeleteButtonContainer > Button:first-child, +.DeleteDiaryModal-ButtonContainer > Button:first-child { + background: $error-darken-1; +} + +/* Botão de cancel */ +.DeleteYesConfirmationModal-DeleteButtonContainer > Button:last-child, +.DeleteDiaryModal-ButtonContainer > Button:last-child { + background: $surface; +} + +/* Estados disabled para botões de delete */ +.DeleteYesConfirmationModal-DeleteButtonContainer > Button:first-child:disabled, +.DeleteDiaryModal-ButtonContainer > Button:first-child:disabled { + background: $surface-lighten-1; + color: $text-muted; +} + +/* Espaçamento específico para labels secundários */ +#DeleteYesConfirmationModal-SecondHeadText { + margin: 1 0; + padding: 0 1; + color: $warning; + text-align: center; + text-style: italic; +} From d9753a375ff18f9992fe9ab6b21f72484022c17b Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Wed, 23 Jul 2025 04:52:22 -0300 Subject: [PATCH 03/12] Add the Diary Settings screen and the CSS for it --- .../ui/screens/diary_settings_screen.py | 176 +++++++++++++++++ src/pilgrim/ui/styles/pilgrim.css | 185 +++++++++++++++++- 2 files changed, 360 insertions(+), 1 deletion(-) create mode 100644 src/pilgrim/ui/screens/diary_settings_screen.py diff --git a/src/pilgrim/ui/screens/diary_settings_screen.py b/src/pilgrim/ui/screens/diary_settings_screen.py new file mode 100644 index 0000000..7b84e1a --- /dev/null +++ b/src/pilgrim/ui/screens/diary_settings_screen.py @@ -0,0 +1,176 @@ + +from textual.widgets import Static +from textual.containers import Container +from textual.widgets import Header, Footer, Label, Button,Checkbox,Input +from textual.screen import Screen +from textual.reactive import reactive +from textual.binding import Binding +from textual import on + +from pilgrim import TravelDiary +from pilgrim.ui.screens.modals.delete_all_entries_from_diary_modal import DeleteAllEntriesModal +from pilgrim.ui.screens.modals.delete_all_photos_from_diary_modal import DeleteAllPhotosModal +from pilgrim.ui.screens.modals.delete_diary_modal import DeleteDiaryModal + + +class SettingsScreen(Screen): + is_changed = reactive(False) + BINDINGS = [ + Binding("escape","cancel","Cancel"), + ] + + def __init__(self,diary:TravelDiary): + super().__init__() + self.current_diary = diary + + self.header = Header() + self.footer = Footer() + self.title = "Settings" + + self.diary_name = Static(self.current_diary.name,id="DiarySettingsScreen-DiaryName") + self.is_the_diary_set_to_auto_open = False + self.diary_entry_count = Static("0") + self.diary_photo_count = Static("0") + self.save_button = Button("Save") + self.cancel_button = Button("Cancel",id="DiarySettingsScreen-cancel_button") + self.apply_button = Button("Apply") + self.backup_diary_button = Button("Backup Diary") + self.delete_diary_button = Button("Delete Diary",id="DiarySettingsScreen-DeleteDiaryButton") + self.delete_all_entries_button = Button("Delete All Entries",id="DiarySettingsScreen-DeleteAllEntriesButton") + self.delete_all_photos_button = Button("Delete All Photos",id="DiarySettingsScreen-DeleteAllPhotosButton") + self.set_auto_open_to_this_diary = Checkbox(id="set_auto_open_to_this_diary",value=self.is_the_diary_set_to_auto_open) + self.delete_diary_button_container = Container( + Label("Delete Diary:"), + + self.delete_diary_button, + id="DiarySettingsScreen-DeleteDiaryButtonContainer", + classes="DiarySettingsScreen-DeleteDiaryButtonContainer Button_Container" + ) + self.backup_diary_button_container = Container( + Label("Backup Diary:"), + self.backup_diary_button, + id="DiarySettingsScreen-BackupDiaryButtonContainer", + classes="DiarySettingsScreen-BackupDiaryButtonContainer Button_Container" + ) + self.delete_all_entries_button_container = Container( + Label("Delete All Entries:"), + self.delete_all_entries_button, + + id="DiarySettingsScreen-DeleteAllEntriesButtonContainer", + classes="DiarySettingsScreen-DeleteAllEntriesButtonContainer Button_Container" + ) + self.delete_all_photos_button_container = Container( + Label("Delete All Photos:"), + self.delete_all_photos_button, + + + id="DiarySettingsScreen-DeleteAllPhotosButtonContainer", + classes="DiarySettingsScreen-DeleteAllPhotosButtonContainer Button_Container" + ) + self.diary_name_container = Container( + Label("Diary Name:"), + self.diary_name, + id="DiarySettingsScreen-DiaryNameContainer", + classes="DiarySettingsScreen-DiaryNameContainer Data_Container" + + ) + self.diary_entry_count_container = Container( + Label("Diary Entries:"), + self.diary_entry_count, + id="DiarySettingsScreen-DiaryEntryCountContainer", + classes="DiarySettingsScreen-DiaryEntryCountContainer Data_Container" + ) + self.set_auto_open_to_this_diary_container = Container( + Label("Set Open This Diary On App Start?:"), + self.set_auto_open_to_this_diary, + id="DiarySettingsScreen-SetAutoOpenToThisDiaryContainer", + classes="DiarySettingsScreen-SetAutoOpenToThisDiaryContainer Data_Container" + + ) + self.diary_photo_count_container = Container( + Label("Diary Photos:"), + self.diary_photo_count, + id="DiarySettingsScreen-DiaryPhotoCountContainer", + classes="DiarySettingsScreen-DiaryPhotoCountContainer Data_Container" + ) + + self.diary_info_container = Container( + + self.diary_name_container, + self.diary_entry_count_container, + self.diary_photo_count_container, + self.set_auto_open_to_this_diary_container, + id="DiarySettingsScreen-DiaryInfoContainer", + classes="DiarySettingsScreen-DiaryInfoContainer", + ) + + self.diary_denger_zone_container = Container( + self.backup_diary_button_container, + self.delete_diary_button_container, + self.delete_all_entries_button_container, + self.delete_all_photos_button_container, + id="DiarySettingsScreen-DiaryDengerZoneContainer", + classes="DiarySettingsScreen-DiaryDengerZoneContainer" + ) + self.button_container = Container( + self.save_button, + self.apply_button, + self.cancel_button, + id="DiarySettingsScreen-ButtonContainer", + classes="DiarySettingsScreen-ButtonContainer" + ) + self.main = Container( + self.diary_info_container, + self.diary_denger_zone_container, + self.button_container, + id="DiarySettingsScreen-MainContainer", + classes="DiarySettingsScreen-MainContainer" + ) + self.diary_info_container.border_title = "Diary Info" + self.diary_denger_zone_container.border_title = "Denger Zone" + + @on(Checkbox.Changed, "#set_auto_open_to_this_diary") + def on_checkbox_changed(self, event): + self.is_changed = not self.is_changed + self.notify("Checkboxed") + + @on(Button.Pressed, "#DiarySettingsScreen-cancel_button") + def on_cancel_button_pressed(self, event): + self.action_cancel() + + @on(Button.Pressed, "#DiarySettingsScreen-DeleteDiaryButton") + def on_delete_diary_button_pressed(self, event): + self.app.push_screen(DeleteDiaryModal(diary_id=self.current_diary.id,diary_name=self.current_diary.name)) + + @on(Button.Pressed, "#DiarySettingsScreen-DeleteAllEntriesButton") + def on_delete_all_entries_button_pressed(self, event): + self.app.push_screen(DeleteAllEntriesModal(diary_id=self.current_diary.id)) + + @on(Button.Pressed, "#DiarySettingsScreen-DeleteAllPhotosButton") + def on_delete_all_photos_button_pressed(self, event): + self.app.push_screen(DeleteAllPhotosModal(diary_id=self.current_diary.id)) + + def action_cancel(self): + if self.is_changed: + self.notify("Cancel button pressed, but changes are not saved",severity="error") + return + self.app.exit() + + + def watch_is_changed(self, value): + label = self.set_auto_open_to_this_diary_container.query_one(Label) + if value: + label.add_class("DiarySettingsScreen-SetAutoOpenToThisDiaryContainer-Not-Saved-Label") + else: + label.remove_class("DiarySettingsScreen-SetAutoOpenToThisDiaryContainer-Not-Saved-Label") + + def compose(self): + yield Header() + yield self.main + yield Footer() + + def on_mount(self): + pass + + def set_checkbox_state(self): + self.set_auto_open_to_this_diary.value = True \ No newline at end of file diff --git a/src/pilgrim/ui/styles/pilgrim.css b/src/pilgrim/ui/styles/pilgrim.css index ce93076..8486ac0 100644 --- a/src/pilgrim/ui/styles/pilgrim.css +++ b/src/pilgrim/ui/styles/pilgrim.css @@ -1,7 +1,8 @@ Screen { layout: vertical; - background: $surface-darken-1; + background: $primary-background-darken-3; align: center middle; + hatch: right $secondary-background-darken-3; } .EditEntryScreen-sub-header { @@ -703,3 +704,185 @@ Screen.-modal { text-align: center; text-style: italic; } + + + + +.Data_Container{ + width: 100%; + layout: grid; + grid-size: 2 1; /* 2 colunas, 1 linha */ + height:auto; + + +} + +.Button_Container{ + width: 100%; + layout: grid; + grid-size: 2 2; + height: auto; + + +} + + +.DiarySettingsScreen-DeleteDiaryButtonContainer{ + grid-size: 2 1; + padding: 0 1; + content-align: center middle; + padding-bottom:1; + +} + +.DiarySettingsScreen-DeleteAllEntriesButtonContainer{ + + margin:0; + padding: 0 1; + height:auto; + padding-bottom:1; + + +} + +.DiarySettingsScreen-DeleteAllPhotosButtonContainer{ + margin:0; + padding: 0 1; + height:auto; + padding-bottom:1; + +} +.DiarySettingsScreen-DeleteAllPhotosButtonContainer > Label, +.DiarySettingsScreen-DeleteAllEntriesButtonContainer > Label{ +color: $error-lighten-3; +padding:1; + +} + +.DiarySettingsScreen-BackupDiaryButtonContainer{ +margin:0; +padding:0 1; +padding-bottom:1 + +} + +.DiarySettingsScreen-BackupDiaryButtonContainer > Label{ +padding:1; +color: $success-darken-1; +} + +.DiarySettingsScreen-BackupDiaryButtonContainer > Button{ + background: $success; +} + +.DiarySettingsScreen-DeleteDiaryButtonContainer > Label{ + color: $error-lighten-3; + content-align: left middle; + padding:1 + + + +} + +.DiarySettingsScreen-DeleteAllPhotosButtonContainer > Button, +.DiarySettingsScreen-DeleteAllEntriesButtonContainer > Button, +.DiarySettingsScreen-DeleteDiaryButtonContainer Button{ + + background: $error; + + } + +.DiarySettingsScreen-MainContainer{ + + align: center top; + layout: vertical; + margin:1; + padding:1; + background: $primary-background + + +} + +.DiarySettingsScreen-DiaryInfoContainer{ + + padding:1 2; + border:round grey; + height:auto; + width: 90%; + padding-bottom: 0; +} + +.DiarySettingsScreen-ButtonContainer{ + height:auto; + layout: grid; + grid-size: 3 1; + grid-gutter:2; + dock:bottom; +} + +.DiarySettingsScreen-ButtonContainer > Button:first-child { + + margin-left:2 + +} + +.DiarySettingsScreen-ButtonContainer > Button:last-child { + + margin-right:2 + +} + +.DiarySettingsScreen-ButtonContainer > Button { + width: 100%; + + + +} + + +#DiarySettingsScreen-DiaryPhotoCountContainer > Static:first-child, +#DiarySettingsScreen-DiaryEntryCountContainer > Static:first-child, +#DiarySettingsScreen-DiaryNameContainer > Static:first-child{ + + text-align: left; + + padding: 0 1; + padding-bottom:1 + +} + + + +#DiarySettingsScreen-DiaryPhotoCountContainer > Static:last-child, +#DiarySettingsScreen-DiaryEntryCountContainer > Static:last-child, +#DiarySettingsScreen-DiaryNameContainer > Static:last-child{ + + text-align: right; + padding:0 1 + +} + +.DiarySettingsScreen-SetAutoOpenToThisDiaryContainer > Checkbox{ + + margin:0; + padding:1; + background: $primary-background; + border:none; + + + +} + +.DiarySettingsScreen-SetAutoOpenToThisDiaryContainer-Not-Saved-Label{ + text-style:bold; + color:$warning-lighten-2; + +} + +.DiarySettingsScreen-DiaryDengerZoneContainer{ + border: round $error-darken-1; + width: 90%; + padding: 0 1; + height: auto; + padding-bottom: 0; +} \ No newline at end of file From ed8619fbf92e964b2683cf15128ca69fdbedd443 Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Wed, 23 Jul 2025 07:18:50 -0300 Subject: [PATCH 04/12] Add the get auto open diary method to the config manager --- src/pilgrim/utils/config_manager.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pilgrim/utils/config_manager.py b/src/pilgrim/utils/config_manager.py index 7a0f856..29b1832 100644 --- a/src/pilgrim/utils/config_manager.py +++ b/src/pilgrim/utils/config_manager.py @@ -46,6 +46,8 @@ class ConfigManager(metaclass=SingletonMeta): if self.__data["settings"]["diary"]["auto_open_diary_on_startup"] == "": self.auto_open_diary = None + else: + self.auto_open_diary = self.__data["settings"]["diary"]["auto_open_diary_on_startup"] self.auto_open_new_diary = self.__data["settings"]["diary"]["auto_open_on_creation"] else: print("Error: config.toml not found.") @@ -103,5 +105,8 @@ class ConfigManager(metaclass=SingletonMeta): def set_auto_open_diary(self, value: str): self.auto_open_diary = value + def get_auto_open_diary(self): + return self.auto_open_diary + def set_auto_open_new_diary(self, value: bool): self.auto_open_new_diary = value From 0c6de1ea2eff23eda0d5a2fe325979f099345f1b Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Wed, 23 Jul 2025 07:26:23 -0300 Subject: [PATCH 05/12] Add the call to the diary settings screen --- src/pilgrim/ui/screens/diary_list_screen.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/pilgrim/ui/screens/diary_list_screen.py b/src/pilgrim/ui/screens/diary_list_screen.py index 649a508..b4912e9 100644 --- a/src/pilgrim/ui/screens/diary_list_screen.py +++ b/src/pilgrim/ui/screens/diary_list_screen.py @@ -9,6 +9,7 @@ from textual.containers import Vertical, Container, Horizontal from pilgrim.models.travel_diary import TravelDiary from pilgrim.ui.screens.about_screen import AboutScreen +from pilgrim.ui.screens.diary_settings_screen import SettingsScreen from pilgrim.ui.screens.edit_diary_modal import EditDiaryModal from pilgrim.ui.screens.new_diary_modal import NewDiaryModal from pilgrim.ui.screens.edit_entry_screen import EditEntryScreen @@ -23,6 +24,7 @@ class DiaryListScreen(Screen): Binding("enter", "open_selected_diary", "Open diary"), Binding("e", "edit_selected_diary", "Edit diary"), Binding("r", "force_refresh", "Force refresh"), + Binding("s", "diary_settings", "Open The Selected Diary Settings"), ] def __init__(self): @@ -285,4 +287,14 @@ class DiaryListScreen(Screen): def action_quit(self): """Action to quit the application""" - self.app.exit() \ No newline at end of file + self.app.exit() + + def action_diary_settings(self): + if self.selected_diary_index is not None: + diary_id = self.diary_id_map.get(self.selected_diary_index) + if diary_id: + self.app.push_screen(SettingsScreen(diary_id=diary_id)) + else: + self.notify("Invalid diary ID") + else: + self.notify("Select a diary to open the settings") From 83fdb885a9da97deec34d18e8c8be9f22d8a500d Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Wed, 23 Jul 2025 07:27:35 -0300 Subject: [PATCH 06/12] Add the feature to set the auto-open diary in the toml file --- .../ui/screens/diary_settings_screen.py | 128 ++++++++++++++++-- 1 file changed, 116 insertions(+), 12 deletions(-) diff --git a/src/pilgrim/ui/screens/diary_settings_screen.py b/src/pilgrim/ui/screens/diary_settings_screen.py index 7b84e1a..a59a1bc 100644 --- a/src/pilgrim/ui/screens/diary_settings_screen.py +++ b/src/pilgrim/ui/screens/diary_settings_screen.py @@ -7,7 +7,7 @@ from textual.reactive import reactive from textual.binding import Binding from textual import on -from pilgrim import TravelDiary +from pilgrim.models.travel_diary import TravelDiary from pilgrim.ui.screens.modals.delete_all_entries_from_diary_modal import DeleteAllEntriesModal from pilgrim.ui.screens.modals.delete_all_photos_from_diary_modal import DeleteAllPhotosModal from pilgrim.ui.screens.modals.delete_diary_modal import DeleteDiaryModal @@ -19,21 +19,22 @@ class SettingsScreen(Screen): Binding("escape","cancel","Cancel"), ] - def __init__(self,diary:TravelDiary): + def __init__(self,diary_id:int): super().__init__() - self.current_diary = diary + self.current_diary = self.app.service_manager.get_travel_diary_service().read_by_id(diary_id) self.header = Header() self.footer = Footer() self.title = "Settings" self.diary_name = Static(self.current_diary.name,id="DiarySettingsScreen-DiaryName") - self.is_the_diary_set_to_auto_open = False - self.diary_entry_count = Static("0") - self.diary_photo_count = Static("0") - self.save_button = Button("Save") + self.notify(str(self.app.config_manager)) + self.is_the_diary_set_to_auto_open = self.app.config_manager.get_auto_open_diary() == self.current_diary.name + self.diary_entry_count = Static(str(len(self.current_diary.entries))) + self.diary_photo_count = Static(str(len(self.current_diary.photos))) + self.save_button = Button("Save",id="DiarySettingsScreen-SaveButton" ) self.cancel_button = Button("Cancel",id="DiarySettingsScreen-cancel_button") - self.apply_button = Button("Apply") + self.apply_button = Button("Apply",id="DiarySettingsScreen-ApplyButton") self.backup_diary_button = Button("Backup Diary") self.delete_diary_button = Button("Delete Diary",id="DiarySettingsScreen-DeleteDiaryButton") self.delete_all_entries_button = Button("Delete All Entries",id="DiarySettingsScreen-DeleteAllEntriesButton") @@ -132,7 +133,7 @@ class SettingsScreen(Screen): @on(Checkbox.Changed, "#set_auto_open_to_this_diary") def on_checkbox_changed(self, event): self.is_changed = not self.is_changed - self.notify("Checkboxed") + @on(Button.Pressed, "#DiarySettingsScreen-cancel_button") def on_cancel_button_pressed(self, event): @@ -154,7 +155,15 @@ class SettingsScreen(Screen): if self.is_changed: self.notify("Cancel button pressed, but changes are not saved",severity="error") return - self.app.exit() + self.dismiss() + + @on(Button.Pressed, "#DiarySettingsScreen-SaveButton") + def on_save_button_pressed(self, event): + self.action_save() + + @on(Button.Pressed, "#DiarySettingsScreen-ApplyButton") + def on_apply_button_pressed(self, event): + self.action_apply() def watch_is_changed(self, value): @@ -170,7 +179,102 @@ class SettingsScreen(Screen): yield Footer() def on_mount(self): - pass + if self.app.config_manager.get_auto_open_diary() == self.current_diary.name: + self.call_after_refresh(self.set_checkbox_state) def set_checkbox_state(self): - self.set_auto_open_to_this_diary.value = True \ No newline at end of file + self.set_auto_open_to_this_diary.value = True + + def _set_auto_open_diary(self,value): + + self.app.config_manager.set_auto_open_diary(value) + self.app.config_manager.save_config() + self.is_changed = False + + def _get_auto_open_diary(self): + return self.app.config_manager.get_auto_open_diary() + + def _make_auto_open_diary_value(self): + value = None + if self.set_auto_open_to_this_diary.value: + value = self.current_diary.name + return value + + + def action_save(self): + + if not self.is_changed: + self.dismiss() + return + + value = self._make_auto_open_diary_value() + current_auto_open = self._get_auto_open_diary() + + + if current_auto_open is None: + self._set_auto_open_diary(value) + self.notify("Settings saved") + self.dismiss() + return + + + if current_auto_open == self.current_diary.name: + if value is None: + + self._set_auto_open_diary(None) + self.notify("Auto-open disabled") + else: + + self.is_changed = False + self.notify("No changes made") + self.dismiss() + return + + + if value is not None: + + self._set_auto_open_diary(value) + self.notify(f"Auto-open changed from '{current_auto_open}' to '{self.current_diary.name}'") + self.dismiss() + else: + + self.is_changed = False + self.notify("No changes made") + self.dismiss() + + + def action_apply(self): + + if not self.is_changed: + return + + value = self._make_auto_open_diary_value() + current_auto_open = self._get_auto_open_diary() + + + if current_auto_open is None: + self._set_auto_open_diary(value) + self.notify("Settings applied") + return + + + if current_auto_open == self.current_diary.name: + if value is None: + + self._set_auto_open_diary(None) + self.notify("Auto-open disabled") + else: + + self.is_changed = False + self.notify("No changes made") + return + + + if value is not None: + + self._set_auto_open_diary(value) + self.notify(f"Auto-open changed from '{current_auto_open}' to '{self.current_diary.name}'") + else: + + self.is_changed = False + self.notify("No changes made") \ No newline at end of file From 4d6c92b4d0117b4aff76577c71f977891f5168ea Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Wed, 23 Jul 2025 07:56:42 -0300 Subject: [PATCH 07/12] Add the feature to delete the whole diary --- .../ui/screens/modals/delete_diary_modal.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/pilgrim/ui/screens/modals/delete_diary_modal.py b/src/pilgrim/ui/screens/modals/delete_diary_modal.py index 61e6597..8930dfd 100644 --- a/src/pilgrim/ui/screens/modals/delete_diary_modal.py +++ b/src/pilgrim/ui/screens/modals/delete_diary_modal.py @@ -5,6 +5,8 @@ from textual.binding import Binding from textual import on + + class DeleteDiaryModal(Screen): BINDINGS = [ @@ -44,11 +46,19 @@ class DeleteDiaryModal(Screen): else: self.delete_button.disabled = True + @on(Button.Pressed,"#DeleteDiaryModal-DeleteButton") def on_delete_button_pressed(self, event): + self.result = True + self._delete_diary() self.dismiss() + from pilgrim.ui.screens.diary_list_screen import DiaryListScreen + + self.app.push_screen(DiaryListScreen()) + + @on(Button.Pressed,"#DeleteDiaryModal-CancelButton") def on_cancel_button_pressed(self, event): @@ -57,3 +67,11 @@ class DeleteDiaryModal(Screen): def action_cancel(self): self.dismiss() + + def _delete_diary(self): + diary = self.app.service_manager.get_travel_diary_service().read_by_id(self.diary_id) + self.app.service_manager.get_travel_diary_service().delete(diary) + if self.app.config_manager.get_auto_open_diary() == self.diary_name: + self.app.config_manager.set_auto_open_diary(None) + self.app.config_manager.save_config() + From 8efda52d15e1f8a0ee7dd4f0d723352f4437dd47 Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Wed, 23 Jul 2025 08:42:00 -0300 Subject: [PATCH 08/12] Add the delete all entries method the travel diary service --- src/pilgrim/service/travel_diary_service.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/pilgrim/service/travel_diary_service.py b/src/pilgrim/service/travel_diary_service.py index 8932b8c..fb00d37 100644 --- a/src/pilgrim/service/travel_diary_service.py +++ b/src/pilgrim/service/travel_diary_service.py @@ -3,6 +3,7 @@ import re import shutil from pathlib import Path +from pilgrim.models.entry import Entry from pilgrim.utils import DirectoryManager from sqlalchemy.exc import IntegrityError @@ -147,3 +148,14 @@ class TravelDiaryService: self.session.rollback() raise ValueError(f"Could not delete diary: {str(e)}") return None + + def delete_all_entries(self,travel_diary: TravelDiary): + diary = self.read_by_id(travel_diary.id) + if diary is not None: + diary.entries = [] + self.session.commit() + + + return True + + return False From ed89669431e1e5d2bc84ca600a68b587c097eff5 Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Wed, 23 Jul 2025 08:44:32 -0300 Subject: [PATCH 09/12] Fix a circular import and passed the proper travel diary id --- .../ui/screens/modals/delete_yes_confirmation_modal.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pilgrim/ui/screens/modals/delete_yes_confirmation_modal.py b/src/pilgrim/ui/screens/modals/delete_yes_confirmation_modal.py index 8c6c9bf..2e779d0 100644 --- a/src/pilgrim/ui/screens/modals/delete_yes_confirmation_modal.py +++ b/src/pilgrim/ui/screens/modals/delete_yes_confirmation_modal.py @@ -5,6 +5,8 @@ from textual.binding import Binding from textual import on + + class DeleteYesConfirmationModal(Screen): BINDINGS = [ Binding("escape", "cancel", "Cancel"), @@ -45,8 +47,9 @@ class DeleteYesConfirmationModal(Screen): self.action_cancel() def action_cancel(self): + from pilgrim.ui.screens.diary_settings_screen import SettingsScreen self.dismiss() - self.app.push_screen(SettingsScreen(diary=travel_diary[0])) + self.app.push_screen(SettingsScreen(self.diary_id)) def compose(self): yield Header() From c5edb835b656f0f398e21de21ac844eb4d993783 Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Wed, 23 Jul 2025 08:45:50 -0300 Subject: [PATCH 10/12] Add the feature to delete all the entries from a travel diary --- .../delete_all_entries_from_diary_modal.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/pilgrim/ui/screens/modals/delete_all_entries_from_diary_modal.py b/src/pilgrim/ui/screens/modals/delete_all_entries_from_diary_modal.py index ec5d5ad..b03db96 100644 --- a/src/pilgrim/ui/screens/modals/delete_all_entries_from_diary_modal.py +++ b/src/pilgrim/ui/screens/modals/delete_all_entries_from_diary_modal.py @@ -3,6 +3,7 @@ from textual.widgets import Button from textual import on + from pilgrim.ui.screens.modals.delete_yes_confirmation_modal import DeleteYesConfirmationModal @@ -10,11 +11,23 @@ class DeleteAllEntriesModal(DeleteYesConfirmationModal): def __init__(self,diary_id:int): super().__init__(diary_id) self.head_text.update("Are you sure you want to delete all entries from this diary?") + self.delete_button.add_class("DeleteDiaryModal-DeleteButton") - @on(Button.Pressed, "#DeleteDiaryModal-DeleteButton") + @on(Button.Pressed, ".DeleteDiaryModal-DeleteButton") def on_delete_button_pressed(self, event): - self.result = True - self.dismiss() + from pilgrim.ui.screens.diary_list_screen import DiaryListScreen + + self.result = True + self._delete_entries() + self.dismiss() + self.app.push_screen(DiaryListScreen()) + + def _delete_entries(self): + diary = self.app.service_manager.get_travel_diary_service().read_by_id(self.diary_id) + if self.app.service_manager.get_travel_diary_service().delete_all_entries(diary): + self.notify("All entries deleted successfully") + else: + self.notify("Failed to delete all entries") From 62e084efb351e8cad894cf2c2e87d9864f73103c Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Thu, 24 Jul 2025 01:54:24 -0300 Subject: [PATCH 11/12] Add the feature to delete all the photos from a travel diary and also remove all the references of photos inside the entry --- src/pilgrim/service/entry_service.py | 22 +++++++++++++++++++ src/pilgrim/service/photo_service.py | 5 +++-- src/pilgrim/service/travel_diary_service.py | 21 ++++++++++++++++++ .../delete_all_photos_from_diary_modal.py | 14 +++++++++++- 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/src/pilgrim/service/entry_service.py b/src/pilgrim/service/entry_service.py index 5826c31..1d6f476 100644 --- a/src/pilgrim/service/entry_service.py +++ b/src/pilgrim/service/entry_service.py @@ -1,3 +1,4 @@ +import re from datetime import datetime from typing import List @@ -54,3 +55,24 @@ class EntryService: self.session.commit() return excluded return None + + + def delete_references_for_specific_photo(self, entry: Entry, photo_hash: str) -> Entry: + regex = r"\[\[photo::" + re.escape(photo_hash) + r"\]\]" + entry.text = re.sub(regex, lambda match: ' ' * len(match.group(0)), entry.text) + + self.session.commit() + self.session.refresh(entry) + + return entry + + def delete_all_photo_references(self, entry: Entry, commit=True) -> Entry: + if not entry.photos: + return entry + photo_hashes = {photo.photo_hash[:8] for photo in entry.photos} + regex = r"\[\[photo::(" + "|".join(re.escape(h) for h in photo_hashes) + r")\]\]" + entry.text = re.sub(regex, lambda match: ' ' * len(match.group(0)), entry.text) + if commit: + self.session.commit() + self.session.refresh(entry) + return entry diff --git a/src/pilgrim/service/photo_service.py b/src/pilgrim/service/photo_service.py index f23ae3f..5ee3a18 100644 --- a/src/pilgrim/service/photo_service.py +++ b/src/pilgrim/service/photo_service.py @@ -141,7 +141,7 @@ class PhotoService: return original return None - def delete(self, photo_src: Photo) -> Photo | None: + def delete(self, photo_src: Photo, commit=True) -> Photo | None: excluded = self.read_by_id(photo_src.id) if excluded: # Store photo data before deletion @@ -162,7 +162,8 @@ class PhotoService: file_path.unlink() self.session.delete(excluded) - self.session.commit() + if commit: + self.session.commit() return deleted_photo return None diff --git a/src/pilgrim/service/travel_diary_service.py b/src/pilgrim/service/travel_diary_service.py index fb00d37..eaedcf0 100644 --- a/src/pilgrim/service/travel_diary_service.py +++ b/src/pilgrim/service/travel_diary_service.py @@ -10,6 +10,9 @@ from sqlalchemy.exc import IntegrityError from pilgrim.models.travel_diary import TravelDiary from unidecode import unidecode +from pilgrim.service.photo_service import PhotoService +from pilgrim.service.entry_service import EntryService + class TravelDiaryService: def __init__(self, session): self.session = session @@ -156,6 +159,24 @@ class TravelDiaryService: self.session.commit() + return True + + return False + + def delete_all_photos(self,travel_diary: TravelDiary): + diary = self.read_by_id(travel_diary.id) + photo_service = PhotoService(self.session) + entry_service = EntryService(self.session) + if diary is not None: + + for entry in list(diary.entries): + entry_service.delete_all_photo_references(entry,commit=False) + + for photo in list(diary.photos): + photo_service.delete(photo,commit=False) + + self.session.commit() + return True return False diff --git a/src/pilgrim/ui/screens/modals/delete_all_photos_from_diary_modal.py b/src/pilgrim/ui/screens/modals/delete_all_photos_from_diary_modal.py index d34b8bb..6b8619d 100644 --- a/src/pilgrim/ui/screens/modals/delete_all_photos_from_diary_modal.py +++ b/src/pilgrim/ui/screens/modals/delete_all_photos_from_diary_modal.py @@ -10,11 +10,23 @@ class DeleteAllPhotosModal(DeleteYesConfirmationModal): def __init__(self,diary_id:int): super().__init__(diary_id) self.head_text.update("Are you sure you want to delete all photos from this diary?") + self.delete_button.add_class("DeleteDiaryModal-DeleteButton") - @on(Button.Pressed, "#DeleteDiaryModal-DeleteButton") + @on(Button.Pressed, ".DeleteDiaryModal-DeleteButton") def on_delete_button_pressed(self, event): + + from pilgrim.ui.screens.diary_list_screen import DiaryListScreen + self.result = True + self._delete_all_photo() self.dismiss() + self.app.push_screen(DiaryListScreen()) + def _delete_all_photo(self): + diary = self.app.service_manager.get_travel_diary_service().read_by_id(self.diary_id) + if self.app.service_manager.get_travel_diary_service().delete_all_photos(diary): + self.notify("All photos deleted successfully") + else: + self.notify("Failed to delete all photos") \ No newline at end of file From 7274d670ec62fd70ae883ed1547648b714c4c191 Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Thu, 24 Jul 2025 02:03:52 -0300 Subject: [PATCH 12/12] Remove the backup diary option on the diary settings screen --- src/pilgrim/ui/screens/diary_settings_screen.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/pilgrim/ui/screens/diary_settings_screen.py b/src/pilgrim/ui/screens/diary_settings_screen.py index a59a1bc..87b418c 100644 --- a/src/pilgrim/ui/screens/diary_settings_screen.py +++ b/src/pilgrim/ui/screens/diary_settings_screen.py @@ -35,7 +35,7 @@ class SettingsScreen(Screen): self.save_button = Button("Save",id="DiarySettingsScreen-SaveButton" ) self.cancel_button = Button("Cancel",id="DiarySettingsScreen-cancel_button") self.apply_button = Button("Apply",id="DiarySettingsScreen-ApplyButton") - self.backup_diary_button = Button("Backup Diary") + self.delete_diary_button = Button("Delete Diary",id="DiarySettingsScreen-DeleteDiaryButton") self.delete_all_entries_button = Button("Delete All Entries",id="DiarySettingsScreen-DeleteAllEntriesButton") self.delete_all_photos_button = Button("Delete All Photos",id="DiarySettingsScreen-DeleteAllPhotosButton") @@ -47,12 +47,6 @@ class SettingsScreen(Screen): id="DiarySettingsScreen-DeleteDiaryButtonContainer", classes="DiarySettingsScreen-DeleteDiaryButtonContainer Button_Container" ) - self.backup_diary_button_container = Container( - Label("Backup Diary:"), - self.backup_diary_button, - id="DiarySettingsScreen-BackupDiaryButtonContainer", - classes="DiarySettingsScreen-BackupDiaryButtonContainer Button_Container" - ) self.delete_all_entries_button_container = Container( Label("Delete All Entries:"), self.delete_all_entries_button,