143 lines
5.4 KiB
Python
143 lines
5.4 KiB
Python
import sys
|
|
import os
|
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
|
|
import logging
|
|
from typing import Iterable
|
|
from textual import on
|
|
from textual.app import App, ComposeResult, SystemCommand
|
|
from textual.logging import TextualHandler
|
|
from textual.screen import Screen
|
|
from textual.widgets import Header, Footer, Static, Label, Input, Button
|
|
from textual.reactive import Reactive
|
|
from textual.binding import Binding
|
|
from textual.timer import Timer
|
|
from textual.containers import ScrollableContainer, Horizontal, Vertical, Grid
|
|
import subprocess
|
|
from maildir_gtd.actions.archive import action_archive
|
|
from maildir_gtd.actions.delete import action_delete
|
|
from maildir_gtd.actions.open import action_open
|
|
from maildir_gtd.actions.show_message import show_message
|
|
from maildir_gtd.actions.next import action_next
|
|
from maildir_gtd.actions.previous import action_previous
|
|
from maildir_gtd.actions.task import action_create_task
|
|
|
|
logging.basicConfig(
|
|
level="NOTSET",
|
|
handlers=[TextualHandler()],
|
|
)
|
|
|
|
|
|
class EmailViewerApp(App):
|
|
"""A simple email viewer app using the Himalaya CLI."""
|
|
title = "Maildir GTD Reader"
|
|
CSS_PATH = "email_viewer.tcss" # Optional: For styling
|
|
|
|
current_message_id: Reactive[int] = Reactive(1)
|
|
|
|
def get_system_commands(self, screen: Screen) -> Iterable[SystemCommand]:
|
|
yield from super().get_system_commands(screen)
|
|
yield SystemCommand("Next Message", "Navigate to Next ID", self.action_next)
|
|
yield SystemCommand("Previous Message", "Navigate to Previous ID", self.action_previous)
|
|
yield SystemCommand("Delete Message", "Delete the current message", self.action_delete)
|
|
yield SystemCommand("Archive Message", "Archive the current message", self.action_archive)
|
|
yield SystemCommand("Open Message", "Open a specific message by ID", self.action_open)
|
|
yield SystemCommand("Create Task", "Create a task using the task CLI", self.action_create_task)
|
|
|
|
BINDINGS = [
|
|
Binding("n", "next", "Next message"),
|
|
Binding("p", "previous", "Previous message"),
|
|
Binding("d", "delete", "Delete message"),
|
|
Binding("a", "archive", "Archive message"),
|
|
Binding("o", "open", "Open message", show=False),
|
|
Binding("q", "quit", "Quit application"),
|
|
Binding("t", "create_task", "Create Task")
|
|
]
|
|
|
|
BINDINGS.extend([
|
|
Binding("j", "scroll_down", "Scroll down"),
|
|
Binding("k", "scroll_up", "Scroll up"),
|
|
Binding("down", "scroll_down", "Scroll down"),
|
|
Binding("up", "scroll_up", "Scroll up"),
|
|
Binding("space", "scroll_page_down", "Scroll page down"),
|
|
Binding("b", "scroll_page_up", "Scroll page up")
|
|
])
|
|
|
|
def compose(self) -> ComposeResult:
|
|
"""Create child widgets for the app."""
|
|
yield Header(show_clock=True)
|
|
yield Footer(Label("[n] Next | [p] Previous | [d] Delete | [a] Archive | [o] Open | [q] Quit | [c] Create Task"))
|
|
yield ScrollableContainer(Static(Label("Email Viewer App"), id="main_content"))
|
|
|
|
def on_mount(self) -> None:
|
|
"""Called when the app is mounted."""
|
|
self.alert_timer: Timer | None = None # Timer to throttle alerts
|
|
# Fetch the ID of the most recent message using the Himalaya CLI
|
|
try:
|
|
result = subprocess.run(
|
|
["himalaya", "envelope", "list", "-o", "json"],
|
|
capture_output=True,
|
|
text=True
|
|
)
|
|
if result.returncode == 0:
|
|
import json
|
|
envelopes = json.loads(result.stdout)
|
|
if envelopes:
|
|
self.current_message_id = int(envelopes[0]['id']) # Get the first envelope's ID
|
|
else:
|
|
self.query_one("#main_content", Static).update("Failed to fetch the most recent message ID.")
|
|
except Exception as e:
|
|
self.query_one("#main_content", Static).update(f"Error: {e}")
|
|
|
|
self.show_message(self.current_message_id)
|
|
|
|
def show_message(self, message_id: int) -> None:
|
|
show_message(self, message_id)
|
|
|
|
def show_status(self, message: str, severity: str = "information") -> None:
|
|
"""Display a status message using the built-in notify function."""
|
|
self.notify(message, title="Status", severity=severity, timeout=1, markup=True)
|
|
|
|
def action_next(self) -> None:
|
|
action_next(self)
|
|
|
|
def action_previous(self) -> None:
|
|
action_previous(self)
|
|
|
|
def action_delete(self) -> None:
|
|
action_delete(self)
|
|
|
|
def action_archive(self) -> None:
|
|
action_archive(self)
|
|
|
|
def action_open(self) -> None:
|
|
action_open(self)
|
|
|
|
def action_create_task(self) -> None:
|
|
action_create_task(self)
|
|
|
|
|
|
def action_scroll_down(self) -> None:
|
|
"""Scroll the main content down."""
|
|
self.query_one("#main_content", Static).scroll_down()
|
|
|
|
def action_scroll_up(self) -> None:
|
|
"""Scroll the main content up."""
|
|
self.query_one("#main_content", Static).scroll_up()
|
|
|
|
def action_scroll_page_down(self) -> None:
|
|
"""Scroll the main content down by a page."""
|
|
self.query_one("#main_content", Static).scroll_page_down()
|
|
|
|
def action_scroll_page_up(self) -> None:
|
|
"""Scroll the main content up by a page."""
|
|
self.query_one("#main_content", Static).scroll_page_up()
|
|
|
|
def action_quit(self) -> None:
|
|
"""Quit the application."""
|
|
self.exit()
|
|
|
|
if __name__ == "__main__":
|
|
app = EmailViewerApp()
|
|
app.run()
|