# history_item.py # # Copyright 2025 Pavel Baksy # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # SPDX-License-Identifier: GPL-3.0-or-later from gi.repository import Gtk, GObject from datetime import datetime @Gtk.Template(resource_path='/cz/vesp/roster/widgets/history-item.ui') class HistoryItem(Gtk.Box): """Widget for displaying a history entry.""" __gtype_name__ = 'HistoryItem' expand_icon = Gtk.Template.Child() details_revealer = Gtk.Template.Child() method_label = Gtk.Template.Child() url_label = Gtk.Template.Child() timestamp_label = Gtk.Template.Child() status_label = Gtk.Template.Child() request_headers_label = Gtk.Template.Child() request_body_label = Gtk.Template.Child() response_headers_label = Gtk.Template.Child() response_body_label = Gtk.Template.Child() load_button = Gtk.Template.Child() __gsignals__ = { 'load-requested': (GObject.SIGNAL_RUN_FIRST, None, ()) } def __init__(self, entry): super().__init__() self.entry = entry self.expanded = False self._populate_ui() def _populate_ui(self): """Populate UI with entry data.""" # Summary self.method_label.set_text(self.entry.request.method) self.url_label.set_text(self.entry.request.url) # Format timestamp try: dt = datetime.fromisoformat(self.entry.timestamp) timestamp_str = dt.strftime("%H:%M:%S") except: timestamp_str = self.entry.timestamp self.timestamp_label.set_text(timestamp_str) # Status if self.entry.response: status_text = f"{self.entry.response.status_code} {self.entry.response.status_text}" self.status_label.set_text(status_text) elif self.entry.error: self.status_label.set_text("Error") # Details - Request headers headers_str = "\n".join([f"{k}: {v}" for k, v in self.entry.request.headers.items()]) if not headers_str: headers_str = "(no headers)" self.request_headers_label.set_text(headers_str) # Details - Request body body_str = self.entry.request.body if self.entry.request.body else "(empty)" # Truncate long bodies if len(body_str) > 200: body_str = body_str[:200] + "..." self.request_body_label.set_text(body_str) # Details - Response if self.entry.response: self.response_headers_label.set_text(self.entry.response.headers) response_body = self.entry.response.body if self.entry.response.body else "(empty)" # Truncate long bodies if len(response_body) > 200: response_body = response_body[:200] + "..." self.response_body_label.set_text(response_body) elif self.entry.error: self.response_headers_label.set_text("(error)") self.response_body_label.set_text(self.entry.error) @Gtk.Template.Callback() def on_clicked(self, gesture, n_press, x, y): """Toggle expansion when clicked.""" self.toggle_expanded() @Gtk.Template.Callback() def on_load_clicked(self, button): """Emit load signal when load button clicked.""" self.emit('load-requested') def toggle_expanded(self): """Toggle between collapsed and expanded view.""" self.expanded = not self.expanded self.details_revealer.set_reveal_child(self.expanded) # Update icon if self.expanded: self.expand_icon.set_from_icon_name('go-down-symbolic') else: self.expand_icon.set_from_icon_name('go-next-symbolic')