Add folder message counts to mail app sidebar

This commit is contained in:
Bendt
2025-12-19 16:24:56 -05:00
parent 98c318af04
commit 9f596b10ae
3 changed files with 87 additions and 6 deletions

View File

@@ -453,7 +453,7 @@ Implement `/` keybinding for search across all apps with similar UX:
5. Calendar: Calendar invites sidebar 5. Calendar: Calendar invites sidebar
6. ~~Mail: Add refresh keybinding~~ (DONE - `r` key) 6. ~~Mail: Add refresh keybinding~~ (DONE - `r` key)
7. ~~Mail: Add mark read/unread action~~ (DONE - `u` key) 7. ~~Mail: Add mark read/unread action~~ (DONE - `u` key)
8. Mail: Folder message counts 8. ~~Mail: Folder message counts~~ (DONE)
9. ~~Mail: URL compression in markdown view~~ (DONE) 9. ~~Mail: URL compression in markdown view~~ (DONE)
10. Mail: Enhance subject styling 10. Mail: Enhance subject styling
11. Mail: Search feature 11. Mail: Search feature

View File

@@ -356,7 +356,13 @@ class EmailViewerApp(App):
try: try:
list_item = event.item list_item = event.item
label = list_item.query_one(Label) label = list_item.query_one(Label)
folder_name = str(label.renderable).strip() folder_text = str(label.renderable).strip()
# Extract folder name (remove count suffix like " [dim](10)[/dim]")
# The format is "FolderName [dim](count)[/dim]" or just "FolderName"
import re
folder_name = re.sub(r"\s*\[dim\]\(\d+\)\[/dim\]$", "", folder_text)
if folder_name and folder_name != self.folder: if folder_name and folder_name != self.folder:
self.folder = folder_name self.folder = folder_name
@@ -492,14 +498,19 @@ class EmailViewerApp(App):
async def fetch_folders(self) -> None: async def fetch_folders(self) -> None:
folders_list = self.query_one("#folders_list", ListView) folders_list = self.query_one("#folders_list", ListView)
folders_list.clear() folders_list.clear()
# Store folder names for count updates
folder_names = ["INBOX"]
# Use the Himalaya client to fetch folders for current account
account = self.current_account if self.current_account else None
folders_list.append( folders_list.append(
ListItem(Label("INBOX", classes="folder_name", markup=False)) ListItem(Label("INBOX", classes="folder_name", markup=True))
) )
try: try:
folders_list.loading = True folders_list.loading = True
# Use the Himalaya client to fetch folders for current account
account = self.current_account if self.current_account else None
folders, success = await himalaya_client.list_folders(account=account) folders, success = await himalaya_client.list_folders(account=account)
if success and folders: if success and folders:
@@ -508,11 +519,12 @@ class EmailViewerApp(App):
# Skip INBOX since we already added it # Skip INBOX since we already added it
if folder_name.upper() == "INBOX": if folder_name.upper() == "INBOX":
continue continue
folder_names.append(folder_name)
item = ListItem( item = ListItem(
Label( Label(
folder_name, folder_name,
classes="folder_name", classes="folder_name",
markup=False, markup=True,
) )
) )
folders_list.append(item) folders_list.append(item)
@@ -523,6 +535,34 @@ class EmailViewerApp(App):
finally: finally:
folders_list.loading = False folders_list.loading = False
# Fetch counts in background and update labels
self._update_folder_counts(folder_names, account)
@work(exclusive=False)
async def _update_folder_counts(
self, folder_names: List[str], account: str | None
) -> None:
"""Fetch and display message counts for folders."""
import asyncio
folders_list = self.query_one("#folders_list", ListView)
async def get_count_for_folder(folder_name: str, index: int):
count, success = await himalaya_client.get_folder_count(
folder_name, account
)
if success and index < len(folders_list.children):
try:
list_item = folders_list.children[index]
label = list_item.query_one(Label)
label.update(f"{folder_name} [dim]({count})[/dim]")
except Exception:
pass # Widget may have been removed
# Fetch counts in parallel
tasks = [get_count_for_folder(name, i) for i, name in enumerate(folder_names)]
await asyncio.gather(*tasks)
def _populate_list_view(self) -> None: def _populate_list_view(self) -> None:
"""Populate the ListView with new items using the new EnvelopeListItem widget.""" """Populate the ListView with new items using the new EnvelopeListItem widget."""
envelopes_list = self.query_one("#envelopes_list", ListView) envelopes_list = self.query_one("#envelopes_list", ListView)

View File

@@ -115,6 +115,47 @@ async def list_folders(
return [], False return [], False
async def get_folder_count(
folder: str,
account: Optional[str] = None,
) -> Tuple[int, bool]:
"""
Get the count of messages in a folder.
Args:
folder: The folder to count messages in
account: The account to use (defaults to default account)
Returns:
Tuple containing:
- Message count
- Success status (True if operation was successful)
"""
try:
# Use a high limit to get all messages, then count them
# This is the most reliable way with himalaya
cmd = f"himalaya envelope list -o json -s 9999 -f '{folder}'"
if account:
cmd += f" -a '{account}'"
process = await asyncio.create_subprocess_shell(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await process.communicate()
if process.returncode == 0:
envelopes = json.loads(stdout.decode())
return len(envelopes), True
else:
logging.error(f"Error getting folder count: {stderr.decode()}")
return 0, False
except Exception as e:
logging.error(f"Exception during folder count: {e}")
return 0, False
async def delete_message( async def delete_message(
message_id: int, message_id: int,
folder: Optional[str] = None, folder: Optional[str] = None,