basically refactored the email viewer
This commit is contained in:
143
maildir_gtd/message_store.py
Normal file
143
maildir_gtd/message_store.py
Normal file
@@ -0,0 +1,143 @@
|
||||
import logging
|
||||
from typing import List, Dict, Any, Tuple, Optional
|
||||
from datetime import datetime, UTC
|
||||
from apis.himalaya import client as himalaya_client
|
||||
|
||||
class MessageStore:
|
||||
"""Store and manage message envelopes"""
|
||||
|
||||
def __init__(self):
|
||||
self.envelopes: List[Dict[str, Any]] = []
|
||||
self.metadata_by_id: Dict[int, Dict[str, Any]] = {}
|
||||
self.total_messages = 0
|
||||
|
||||
def load(self, envelopes: List[Dict[str, Any]], sort_ascending: bool = True) -> None:
|
||||
"""Load envelopes from Himalaya client and process them"""
|
||||
if not envelopes:
|
||||
self.envelopes = []
|
||||
self.metadata_by_id = {}
|
||||
self.total_messages = 0
|
||||
return
|
||||
|
||||
# Sort by date
|
||||
envelopes.sort(
|
||||
key=lambda x: x.get("date", ""),
|
||||
reverse=not sort_ascending,
|
||||
)
|
||||
|
||||
# Group envelopes by month
|
||||
grouped_envelopes = []
|
||||
months = {}
|
||||
|
||||
for envelope in envelopes:
|
||||
if "id" not in envelope:
|
||||
continue
|
||||
|
||||
# Extract date and determine month group
|
||||
date_str = envelope.get("date", "")
|
||||
try:
|
||||
date = datetime.fromisoformat(date_str.replace("Z", "+00:00"))
|
||||
month_key = date.strftime("%B %Y")
|
||||
except (ValueError, TypeError):
|
||||
month_key = "Unknown Date"
|
||||
|
||||
# Add month header if this is a new month
|
||||
if month_key not in months:
|
||||
months[month_key] = True
|
||||
grouped_envelopes.append({"type": "header", "label": month_key})
|
||||
|
||||
# Add the envelope
|
||||
grouped_envelopes.append(envelope)
|
||||
|
||||
# Store metadata for quick access
|
||||
envelope_id = int(envelope["id"])
|
||||
self.metadata_by_id[envelope_id] = {
|
||||
"id": envelope_id,
|
||||
"subject": envelope.get("subject", ""),
|
||||
"from": envelope.get("from", {}),
|
||||
"to": envelope.get("to", {}),
|
||||
"cc": envelope.get("cc", {}),
|
||||
"date": date_str,
|
||||
"index": len(grouped_envelopes) - 1,
|
||||
}
|
||||
|
||||
self.envelopes = grouped_envelopes
|
||||
self.total_messages = len(self.metadata_by_id)
|
||||
|
||||
async def reload(self, sort_ascending: bool = True) -> None:
|
||||
"""Reload envelopes from the Himalaya client"""
|
||||
envelopes, success = await himalaya_client.list_envelopes()
|
||||
if success:
|
||||
self.load(envelopes, sort_ascending)
|
||||
else:
|
||||
logging.error("Failed to reload envelopes")
|
||||
|
||||
def get_metadata(self, message_id: int) -> Optional[Dict[str, Any]]:
|
||||
"""Get metadata for a message by ID"""
|
||||
return self.metadata_by_id.get(message_id)
|
||||
|
||||
def find_next_valid_id(self, current_index: int) -> Tuple[Optional[int], Optional[int]]:
|
||||
"""Find the next valid message ID and its index"""
|
||||
if not self.envelopes or current_index >= len(self.envelopes) - 1:
|
||||
return None, None
|
||||
|
||||
# Start from current index + 1
|
||||
for idx in range(current_index + 1, len(self.envelopes)):
|
||||
item = self.envelopes[idx]
|
||||
# Skip header items
|
||||
if item and item.get("type") != "header" and "id" in item:
|
||||
return int(item["id"]), idx
|
||||
|
||||
return None, None
|
||||
|
||||
def find_prev_valid_id(self, current_index: int) -> Tuple[Optional[int], Optional[int]]:
|
||||
"""Find the previous valid message ID and its index"""
|
||||
if not self.envelopes or current_index <= 0:
|
||||
return None, None
|
||||
|
||||
# Start from current index - 1
|
||||
for idx in range(current_index - 1, -1, -1):
|
||||
item = self.envelopes[idx]
|
||||
# Skip header items
|
||||
if item and item.get("type") != "header" and "id" in item:
|
||||
return int(item["id"]), idx
|
||||
|
||||
return None, None
|
||||
|
||||
def get_oldest_id(self) -> int:
|
||||
"""Get the ID of the oldest message (first non-header item)"""
|
||||
for item in self.envelopes:
|
||||
if item and item.get("type") != "header" and "id" in item:
|
||||
return int(item["id"])
|
||||
return 0
|
||||
|
||||
def get_newest_id(self) -> int:
|
||||
"""Get the ID of the newest message (last non-header item)"""
|
||||
for item in reversed(self.envelopes):
|
||||
if item and item.get("type") != "header" and "id" in item:
|
||||
return int(item["id"])
|
||||
return 0
|
||||
|
||||
def remove_envelope(self, message_id: int) -> None:
|
||||
"""Remove an envelope from the store"""
|
||||
metadata = self.metadata_by_id.get(message_id)
|
||||
if not metadata:
|
||||
return
|
||||
|
||||
index = metadata["index"]
|
||||
if 0 <= index < len(self.envelopes):
|
||||
# Remove from the envelopes list
|
||||
self.envelopes.pop(index)
|
||||
|
||||
# Remove from metadata dictionary
|
||||
del self.metadata_by_id[message_id]
|
||||
|
||||
# Update indexes for all subsequent messages
|
||||
for id_, meta in self.metadata_by_id.items():
|
||||
if meta["index"] > index:
|
||||
meta["index"] -= 1
|
||||
|
||||
# Update total message count
|
||||
self.total_messages = len(self.metadata_by_id)
|
||||
else:
|
||||
logging.warning(f"Invalid index {index} for message ID {message_id}")
|
||||
Reference in New Issue
Block a user