Files
luk/maildir_gtd/widgets/ContentContainer.py
2025-05-14 15:11:24 -06:00

155 lines
5.7 KiB
Python

import asyncio
import logging
from datetime import datetime
import re
from textual.widgets import Static, Markdown, Label
from textual.containers import Vertical, Horizontal, ScrollableContainer
from textual import work
from textual.worker import Worker
from apis.himalaya import client as himalaya_client
class EnvelopeHeader(Vertical):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.subject_label = Label("")
self.from_label = Label("")
self.to_label = Label("")
self.date_label = Label("")
self.cc_label = Label("")
def on_mount(self):
self.styles.height = "auto"
self.mount(self.subject_label)
self.mount(self.from_label)
self.mount(self.to_label)
self.mount(self.cc_label)
self.mount(self.date_label)
def update(self, subject, from_, to, date, cc=None):
self.subject_label.update(f"[b]Subject:[/b] {subject}")
self.from_label.update(f"[b]From:[/b] {from_}")
self.to_label.update(f"[b]To:[/b] {to}")
# Format the date for better readability
if date:
try:
# Try to convert the date string to a datetime object
date_obj = datetime.fromisoformat(date.replace('Z', '+00:00'))
formatted_date = date_obj.strftime("%a, %d %b %Y %H:%M:%S %Z")
self.date_label.update(f"[b]Date:[/b] {formatted_date}")
except (ValueError, TypeError):
# If parsing fails, just use the original date string
self.date_label.update(f"[b]Date:[/b] {date}")
else:
self.date_label.update("[b]Date:[/b] Unknown")
if cc:
self.cc_label.update(f"[b]CC:[/b] {cc}")
self.cc_label.styles.display = "block"
else:
self.cc_label.styles.display = "none"
class ContentContainer(ScrollableContainer):
can_focus = True
def __init__(self, **kwargs):
super().__init__(**kwargs)
# self.header = EnvelopeHeader(id="envelope_header")
self.content = Markdown("", id="markdown_content")
self.html_content = Static("", id="html_content", markup=False)
self.current_mode = "html" # Default to HTML mode
self.current_content = None
self.current_message_id = None
self.content_worker = None
def compose(self):
yield self.content
yield self.html_content
def on_mount(self):
# Hide markdown content initially
self.content.styles.display = "none"
self.html_content.styles.display = "block"
async def toggle_mode(self):
"""Toggle between plaintext and HTML viewing modes."""
if self.current_mode == "html":
self.current_mode = "text"
self.html_content.styles.display = "none"
self.content.styles.display = "block"
else:
self.current_mode = "html"
self.content.styles.display = "none"
self.html_content.styles.display = "block"
# Reload the content if we have a message ID
if self.current_message_id:
self.display_content(self.current_message_id)
# def update_header(self, subject, from_, to, date, cc=None):
# self.header.update(subject, from_, to, date, cc)
@work(exclusive=True)
async def fetch_message_content(self, message_id: int, format: str):
"""Fetch message content using the Himalaya client module."""
if not message_id:
self.notify("No message ID provided.")
return
content, success = await himalaya_client.get_message_content(message_id, format)
if success:
self._update_content(content)
else:
self.notify(f"Failed to fetch content for message ID {message_id}.")
def display_content(self, message_id: int) -> None:
"""Display the content of a message."""
if not message_id:
return
self.current_message_id = message_id
# Cancel any existing content fetch operations
if self.content_worker:
self.content_worker.cancel()
# Fetch content in the current mode
format_type = "text" if self.current_mode == "text" else "html"
self.content_worker = self.fetch_message_content(message_id, format_type)
def _update_content(self, content: str) -> None:
"""Update the content widgets with the fetched content."""
try:
if self.current_mode == "text":
# For text mode, use the Markdown widget
self.content.update(content)
else:
# For HTML mode, use the Static widget with markup
# First, try to extract the body content if it's HTML
body_match = re.search(r'<body[^>]*>(.*?)</body>', content, re.DOTALL | re.IGNORECASE)
if body_match:
content = body_match.group(1)
# Replace some common HTML elements with Textual markup
content = content.replace('<b>', '[b]').replace('</b>', '[/b]')
content = content.replace('<i>', '[i]').replace('</i>', '[/i]')
content = content.replace('<u>', '[u]').replace('</u>', '[/u]')
# Convert links to a readable format
content = re.sub(r'<a href="([^"]+)"[^>]*>([^<]+)</a>', r'[\2](\1)', content)
# Add CSS for better readability
self.html_content.update(content)
except Exception as e:
logging.error(f"Error updating content: {e}")
if self.current_mode == "text":
self.content.update(f"Error displaying content: {e}")
else:
self.html_content.update(f"Error displaying content: {e}")