bug fix display and load

This commit is contained in:
Bendt
2025-12-18 13:29:56 -05:00
parent 4dbb7c5fea
commit 8244bd94c9
7 changed files with 68 additions and 11 deletions

BIN
.coverage

Binary file not shown.

View File

@@ -22,7 +22,7 @@ async def delete_current(app):
next_id, next_idx = app.message_store.find_prev_valid_id(current_index) next_id, next_idx = app.message_store.find_prev_valid_id(current_index)
# Delete the message using our Himalaya client module # Delete the message using our Himalaya client module
success = await himalaya_client.delete_message(current_message_id) message, success = await himalaya_client.delete_message(current_message_id)
if success: if success:
app.show_status(f"Message {current_message_id} deleted.", "success") app.show_status(f"Message {current_message_id} deleted.", "success")
@@ -38,4 +38,6 @@ async def delete_current(app):
app.current_message_id = 0 app.current_message_id = 0
app.show_status("No more messages available.", "warning") app.show_status("No more messages available.", "warning")
else: else:
app.show_status(f"Failed to delete message {current_message_id}.", "error") app.show_status(
f"Failed to delete message {current_message_id}: {message}", "error"
)

View File

@@ -250,8 +250,16 @@ class EmailViewerApp(App):
if event.list_view.index is None: if event.list_view.index is None:
return return
# Only handle selection from the envelopes list
if event.list_view.id != "envelopes_list":
return
selected_index = event.list_view.index selected_index = event.list_view.index
# Check bounds before accessing
if selected_index < 0 or selected_index >= len(self.message_store.envelopes):
return
current_item = self.message_store.envelopes[selected_index] current_item = self.message_store.envelopes[selected_index]
if current_item is None or current_item.get("type") == "header": if current_item is None or current_item.get("type") == "header":
@@ -261,13 +269,26 @@ class EmailViewerApp(App):
self.current_message_id = message_id self.current_message_id = message_id
self.current_message_index = selected_index self.current_message_index = selected_index
# Focus the main content panel after selecting a message
self.action_focus_4()
def on_list_view_highlighted(self, event: ListView.Highlighted) -> None: def on_list_view_highlighted(self, event: ListView.Highlighted) -> None:
"""Called when an item in the list view is highlighted (e.g., via arrow keys).""" """Called when an item in the list view is highlighted (e.g., via arrow keys)."""
if event.list_view.index is None: if event.list_view.index is None:
return return
# Only handle highlights from the envelopes list
if event.list_view.id != "envelopes_list":
return
highlighted_index = event.list_view.index highlighted_index = event.list_view.index
# Check bounds before accessing
if highlighted_index < 0 or highlighted_index >= len(
self.message_store.envelopes
):
return
current_item = self.message_store.envelopes[highlighted_index] current_item = self.message_store.envelopes[highlighted_index]
if current_item is None or current_item.get("type") == "header": if current_item is None or current_item.get("type") == "header":

View File

@@ -89,6 +89,13 @@ class LinkPanelConfig(BaseModel):
close_on_open: bool = False close_on_open: bool = False
class MailConfig(BaseModel):
"""Configuration for mail operations."""
# Folder to move messages to when archiving
archive_folder: str = "Archive"
class ThemeConfig(BaseModel): class ThemeConfig(BaseModel):
"""Theme/appearance settings.""" """Theme/appearance settings."""
@@ -104,6 +111,7 @@ class MaildirGTDConfig(BaseModel):
) )
content_display: ContentDisplayConfig = Field(default_factory=ContentDisplayConfig) content_display: ContentDisplayConfig = Field(default_factory=ContentDisplayConfig)
link_panel: LinkPanelConfig = Field(default_factory=LinkPanelConfig) link_panel: LinkPanelConfig = Field(default_factory=LinkPanelConfig)
mail: MailConfig = Field(default_factory=MailConfig)
keybindings: KeybindingsConfig = Field(default_factory=KeybindingsConfig) keybindings: KeybindingsConfig = Field(default_factory=KeybindingsConfig)
theme: ThemeConfig = Field(default_factory=ThemeConfig) theme: ThemeConfig = Field(default_factory=ThemeConfig)

View File

@@ -11,17 +11,23 @@
width: 1fr width: 1fr
} }
.list_view {
height: 3;
}
#main_content { #main_content {
width: 2fr; width: 2fr;
} }
.envelope-selected { .envelope-selected {
tint: $accent 20%; tint: $accent 20%;
} }
#sidebar:focus-within { #sidebar:focus-within {
background: $panel; background: $panel;
.list_view:blur { .list_view:blur {
height: 3; height: 3;
} }
@@ -30,6 +36,11 @@
} }
} }
#envelopes_list {
height: 2fr;
}
#main_content:focus, .list_view:focus { #main_content:focus, .list_view:focus {
border: round $secondary; border: round $secondary;
background: rgb(55, 53, 57); background: rgb(55, 53, 57);

View File

@@ -147,6 +147,9 @@ class EnvelopeListItem(Static):
date_str = date_str.replace("Z", "+00:00") date_str = date_str.replace("Z", "+00:00")
dt = datetime.fromisoformat(date_str) dt = datetime.fromisoformat(date_str)
# Convert to local timezone
dt = dt.astimezone()
parts = [] parts = []
if self.config.show_date: if self.config.show_date:
parts.append(dt.strftime(self.config.date_format)) parts.append(dt.strftime(self.config.date_format))

View File

@@ -4,6 +4,8 @@ import json
import logging import logging
import subprocess import subprocess
from src.maildir_gtd.config import get_config
async def list_envelopes(limit: int = 9999) -> Tuple[List[Dict[str, Any]], bool]: async def list_envelopes(limit: int = 9999) -> Tuple[List[Dict[str, Any]], bool]:
""" """
@@ -92,7 +94,7 @@ async def list_folders() -> Tuple[List[Dict[str, Any]], bool]:
return [], False return [], False
async def delete_message(message_id: int) -> bool: async def delete_message(message_id: int) -> Tuple[Optional[str], bool]:
""" """
Delete a message by its ID. Delete a message by its ID.
@@ -100,7 +102,9 @@ async def delete_message(message_id: int) -> bool:
message_id: The ID of the message to delete message_id: The ID of the message to delete
Returns: Returns:
True if deletion was successful, False otherwise Tuple containing:
- Result message or error
- Success status (True if deletion was successful)
""" """
try: try:
process = await asyncio.create_subprocess_shell( process = await asyncio.create_subprocess_shell(
@@ -110,10 +114,15 @@ async def delete_message(message_id: int) -> bool:
) )
stdout, stderr = await process.communicate() stdout, stderr = await process.communicate()
return process.returncode == 0 if process.returncode == 0:
return stdout.decode().strip() or "Deleted successfully", True
else:
error_msg = stderr.decode().strip()
logging.error(f"Error deleting message: {error_msg}")
return error_msg or "Unknown error", False
except Exception as e: except Exception as e:
logging.error(f"Exception during message deletion: {e}") logging.error(f"Exception during message deletion: {e}")
return False return str(e), False
# async def archive_message(message_id: int) -> [str, bool]: # async def archive_message(message_id: int) -> [str, bool]:
@@ -151,8 +160,10 @@ async def archive_messages(message_ids: List[str]) -> Tuple[Optional[str], bool]
A tuple containing an optional output string and a boolean indicating success. A tuple containing an optional output string and a boolean indicating success.
""" """
try: try:
config = get_config()
archive_folder = config.mail.archive_folder
ids_str = " ".join(message_ids) ids_str = " ".join(message_ids)
cmd = f"himalaya message move Archives {ids_str}" cmd = f"himalaya message move {archive_folder} {ids_str}"
process = await asyncio.create_subprocess_shell( process = await asyncio.create_subprocess_shell(
cmd, cmd,
@@ -162,13 +173,14 @@ async def archive_messages(message_ids: List[str]) -> Tuple[Optional[str], bool]
stdout, stderr = await process.communicate() stdout, stderr = await process.communicate()
if process.returncode == 0: if process.returncode == 0:
return stdout.decode(), True return stdout.decode().strip() or "Archived successfully", True
else: else:
logging.error(f"Error archiving messages: {stderr.decode()}") error_msg = stderr.decode().strip()
return None, False logging.error(f"Error archiving messages: {error_msg}")
return error_msg or "Unknown error", False
except Exception as e: except Exception as e:
logging.error(f"Exception during message archiving: {e}") logging.error(f"Exception during message archiving: {e}")
return None, False return str(e), False
async def get_message_content(message_id: int) -> Tuple[Optional[str], bool]: async def get_message_content(message_id: int) -> Tuple[Optional[str], bool]: