Add folder message counts to mail app sidebar
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user