diff --git a/src/history_manager.py b/src/history_manager.py index b527854..b9b39ee 100644 --- a/src/history_manager.py +++ b/src/history_manager.py @@ -78,3 +78,18 @@ class HistoryManager: entries = entries[:100] self.save_history(entries) + + def delete_entry(self, entry: HistoryEntry): + """Delete a specific entry from history.""" + entries = self.load_history() + # Filter out the entry by comparing timestamps and URLs + entries = [e for e in entries if not ( + e.timestamp == entry.timestamp and + e.request.url == entry.request.url and + e.request.method == entry.request.method + )] + self.save_history(entries) + + def clear_history(self): + """Clear all history entries.""" + self.save_history([]) diff --git a/src/main.py b/src/main.py index 98e4a04..f846adc 100644 --- a/src/main.py +++ b/src/main.py @@ -68,8 +68,10 @@ class RosterApplication(Adw.Application): def on_preferences_action(self, widget, _): """Callback for the app.preferences action.""" - preferences = PreferencesDialog() - preferences.set_transient_for(self.props.active_window) + window = self.props.active_window + preferences = PreferencesDialog(history_manager=window.history_manager) + preferences.set_transient_for(window) + preferences.connect('history-cleared', lambda d: window._load_history()) preferences.present() def on_shortcuts_action(self, widget, _): diff --git a/src/preferences-dialog.ui b/src/preferences-dialog.ui index 9c175be..578bf37 100644 --- a/src/preferences-dialog.ui +++ b/src/preferences-dialog.ui @@ -43,6 +43,30 @@ + + + + History + Manage request history + + + + Clear All History + Remove all saved request and response history + + + Clear + center + + + + + + + + diff --git a/src/preferences_dialog.py b/src/preferences_dialog.py index 6f97848..5be345b 100644 --- a/src/preferences_dialog.py +++ b/src/preferences_dialog.py @@ -17,7 +17,7 @@ # # SPDX-License-Identifier: GPL-3.0-or-later -from gi.repository import Adw, Gtk, Gio +from gi.repository import Adw, Gtk, Gio, GObject @Gtk.Template(resource_path='/cz/vesp/roster/preferences-dialog.ui') @@ -26,10 +26,17 @@ class PreferencesDialog(Adw.PreferencesWindow): tls_verification_row = Gtk.Template.Child() timeout_row = Gtk.Template.Child() + clear_history_button = Gtk.Template.Child() - def __init__(self, **kwargs): + __gsignals__ = { + 'history-cleared': (GObject.SIGNAL_RUN_FIRST, None, ()) + } + + def __init__(self, history_manager=None, **kwargs): super().__init__(**kwargs) + self.history_manager = history_manager + # Get settings self.settings = Gio.Settings.new('cz.vesp.roster') @@ -47,3 +54,30 @@ class PreferencesDialog(Adw.PreferencesWindow): 'value', Gio.SettingsBindFlags.DEFAULT ) + + @Gtk.Template.Callback() + def on_clear_history_clicked(self, button): + """Clear all history after confirmation.""" + if not self.history_manager: + return + + # Create confirmation dialog + dialog = Adw.AlertDialog.new( + "Clear All History?", + "This will permanently delete all saved request and response history. This action cannot be undone." + ) + dialog.add_response("cancel", "Cancel") + dialog.add_response("clear", "Clear History") + dialog.set_response_appearance("clear", Adw.ResponseAppearance.DESTRUCTIVE) + dialog.set_default_response("cancel") + dialog.set_close_response("cancel") + + def on_response(dialog, response): + if response == "clear": + # Clear the history + self.history_manager.clear_history() + # Notify the main window to refresh + self.emit('history-cleared') + + dialog.connect("response", on_response) + dialog.present(self) diff --git a/src/widgets/history-item.ui b/src/widgets/history-item.ui index d63ef26..de60bd2 100644 --- a/src/widgets/history-item.ui +++ b/src/widgets/history-item.ui @@ -62,6 +62,20 @@ + + + + user-trash-symbolic + Delete this history item + center + + + + + diff --git a/src/widgets/history_item.py b/src/widgets/history_item.py index 70158bf..5e2db0d 100644 --- a/src/widgets/history_item.py +++ b/src/widgets/history_item.py @@ -33,6 +33,7 @@ class HistoryItem(Gtk.Box): url_label = Gtk.Template.Child() timestamp_label = Gtk.Template.Child() status_label = Gtk.Template.Child() + delete_button = Gtk.Template.Child() request_headers_label = Gtk.Template.Child() request_body_scroll = Gtk.Template.Child() request_body_text = Gtk.Template.Child() @@ -48,7 +49,8 @@ class HistoryItem(Gtk.Box): load_button = Gtk.Template.Child() __gsignals__ = { - 'load-requested': (GObject.SIGNAL_RUN_FIRST, None, ()) + 'load-requested': (GObject.SIGNAL_RUN_FIRST, None, ()), + 'delete-requested': (GObject.SIGNAL_RUN_FIRST, None, ()) } def __init__(self, entry): @@ -140,6 +142,11 @@ class HistoryItem(Gtk.Box): """Emit load signal when load button clicked.""" self.emit('load-requested') + @Gtk.Template.Callback() + def on_delete_clicked(self, button): + """Emit delete signal when delete button clicked.""" + self.emit('delete-requested') + @Gtk.Template.Callback() def on_request_expander_clicked(self, gesture, n_press, x, y): """Toggle request body expansion.""" diff --git a/src/window.py b/src/window.py index c0ea580..4e29851 100644 --- a/src/window.py +++ b/src/window.py @@ -437,6 +437,7 @@ class RosterWindow(Adw.ApplicationWindow): for entry in entries: item = HistoryItem(entry) item.connect('load-requested', self._on_history_load_requested, entry) + item.connect('delete-requested', self._on_history_delete_requested, entry) self.history_listbox.append(item) def _on_history_load_requested(self, widget, entry): @@ -444,6 +445,13 @@ class RosterWindow(Adw.ApplicationWindow): # Smart loading: replaces empty tabs or creates new tab if modified self._load_request_from_entry(entry) + def _on_history_delete_requested(self, widget, entry): + """Handle delete request from history item.""" + # Delete the entry from history + self.history_manager.delete_entry(entry) + # Refresh the history panel + self._load_history() + def _load_request_from_entry(self, entry): """Load request from history entry - smart loading based on current tab state.""" request = entry.request