from textual.reactive import Reactive from textual.app import ComposeResult from textual.widgets import Label from textual.containers import Horizontal, ScrollableContainer from datetime import datetime import re from datetime import UTC class EnvelopeHeader(ScrollableContainer): subject = Reactive("") from_ = Reactive("") to = Reactive("") date = Reactive("") cc = Reactive("") bcc = Reactive("") """Header for the email viewer.""" def on_mount(self) -> None: """Mount the header.""" def compose(self) -> ComposeResult: yield Horizontal( Label("Subject:", classes="header_key"), Label(self.subject, classes="header_value", markup=False, id="subject"), ) yield Horizontal( Label("Date:", classes="header_key"), Label(self.date, classes="header_value", markup=False, id="date"), ) # yield Horizontal( # Label("From:", classes="header_key"), # Label(self.from_, # classes="header_value", markup=False, id="from"), # ) # yield Horizontal( # Label("To:", classes="header_key"), # Label(self.to, classes="header_value", # markup=False, id="to"), # ) # yield Horizontal( # ) # yield Horizontal( # Label("CC:", classes="header_key"), # Label(self.cc, classes="header_value", # markup=False, id="cc"), # ) def watch_subject(self, subject: str) -> None: """Watch the subject for changes.""" self.query_one("#subject", Label).update(subject) # def watch_to(self, to: str) -> None: # """Watch the to field for changes.""" # self.query_one("#to").update(to) # def watch_from(self, from_: str) -> None: # """Watch the from field for changes.""" # self.query_one("#from").update(from_) def watch_date(self, date: str) -> None: """Watch the date for changes and convert to local timezone.""" if date: try: # If date already has timezone info, parse it if any(x in date for x in ['+', '-', 'Z']): # Try parsing with timezone info try: # Handle ISO format with Z suffix if 'Z' in date: parsed_date = datetime.fromisoformat( date.replace('Z', '+00:00')) else: parsed_date = datetime.fromisoformat(date) except ValueError: # Try another common format parsed_date = datetime.strptime( date, "%Y-%m-%d %H:%M%z") else: # No timezone info, assume UTC try: parsed_date = datetime.strptime( date, "%Y-%m-%d %H:%M").replace(tzinfo=UTC) except ValueError: # If regular parsing fails, try to extract date cmpnts match = re.search( r"(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})", date) if match: date_part, time_part = match.groups() parsed_date = datetime.strptime( f"{date_part} {time_part}", "%Y-%m-%d %H:%M" ).replace(tzinfo=UTC) else: # If all else fails, just use the original string self.query_one("#date", Label).update(date) return # Convert to local timezone local_date = parsed_date.astimezone() # Format for display formatted_date = local_date.strftime("%a %b %d %H:%M (%Z)") self.query_one("#date", Label).update(formatted_date) except Exception: # If parsing fails, just display the original date self.query_one("#date", Label).update(f"{date}") else: self.query_one("#date", Label).update("") # def watch_cc(self, cc: str) -> None: # """Watch the cc field for changes.""" # self.query_one("#cc").update(cc)