From f4903495e37b19de3f5de7bdb02f860ad8203c16 Mon Sep 17 00:00:00 2001 From: Tim Bendt Date: Mon, 5 May 2025 17:08:01 -0600 Subject: [PATCH] navigation and archive behavior fixed --- maildir_gtd/__pycache__/utils.cpython-311.pyc | Bin 2541 -> 2571 bytes .../__pycache__/archive.cpython-311.pyc | Bin 2548 -> 2466 bytes .../__pycache__/delete.cpython-311.pyc | Bin 2166 -> 2084 bytes maildir_gtd/actions/archive.py | 4 +- maildir_gtd/actions/delete.py | 4 +- maildir_gtd/app.py | 89 ++++++++++-------- maildir_gtd/email_viewer.tcss | 2 +- maildir_gtd/utils.py | 4 +- 8 files changed, 56 insertions(+), 47 deletions(-) diff --git a/maildir_gtd/__pycache__/utils.cpython-311.pyc b/maildir_gtd/__pycache__/utils.cpython-311.pyc index b06eb6a4f51db1663e32ff9cdcd2318242dbe7c2..07626f1dbd155f66f4d022b1ec44142c005233f9 100644 GIT binary patch delta 505 zcmaDW+%3YloR^o20SJ7bNo0t!P2`h^2m|t_Go&!2Fy=7iGDI=tGDa~ng7{21Oi@fg znmLC#mnDiNmo#!lf(;i_RtK{9h0GXukFAclZi))Gb-8>o*1#;#$Vyq3wFk#q7# zM$yT?nB;&gM@Es!y3E3iC7T_Y_cAf^PyWw3OQ?!JIz~H2H#R0ECdCRw+?u?AO_Pyp z@+mfTA*Q0#Tg=6!Nw+wPONt6gGILWWGqSJd(gj+=3dF@#lee+IH(_Vh{=fhvIyml1 zNX=opAaA(AXhX#TwkziD7bQHdNO*Mc-jz_eBB8lL_kyM8MG3Df5?+&UaK!UzvKN8; zQN#iwR3o-vWJ?y9iIV`^0|1rtbm#y8 delta 457 zcmeAcc`MAfoR^o20SKg2Br-0uPUMpa@B{LuGo&!2Fy=7iGDI=tGDa~ng7{21Ou5Wa z%(*O4EI>YUDsw7}BuFb5q_Cj!(^yhiTUer4Q`jcfDNLMi&RDYXdK+V8SuINmBTNqi zLlzs1T?15)Fl_-lOdOd`;ampPyBe-Pg$to$@&+bzMvloZn2dp>46{08`DPd9y-bXJ zlbP6NvHxP!E@EO}n7ocnlaX`s4K{TFj^dJ{f|AVK)LX2@1v!}|lLgsVbLjwWV*%pg z%E`yr-y5^BYJXq=5*-|OB^0hmXs*!MV0J~z>bjQ0B`t>o!WXq%uV}d*NW7xualtF< zqD1r+iRj5MIpP)lG}(%nfyNYp%(}%^Qdy9hmtMpQ5&`+QNM~{`rxceykO}fiapvTS noJy1Ta7vgfGs-R~S>dvz>H`CixglkT$(AfI6DLt54Kx7&V^(9e diff --git a/maildir_gtd/actions/__pycache__/archive.cpython-311.pyc b/maildir_gtd/actions/__pycache__/archive.cpython-311.pyc index 0e567e35cb4548cc25a9193f43d96da285c5384c..914c7e87a803739e7137b47b440d1276aacfe552 100644 GIT binary patch delta 297 zcmew&yhxaLIWI340}y<4lFYcVkynqA(P6U<<2^>!kQ#;syps)?L?#JLS(cf-hGlXilbXz2rWB54ObiUGffxc98B$n+8I&1HlqUPLXmWDau+C-xshZr# zqRYrNc|A+@WPMg0Mxn_utb2tyT`Q7P3raHc^EBCRF((!j6v<7tWNYS90h+}J#KpTe zA7j&Jl<9Ep_wDkXt8tM>`3eYWUI3#HYz({tla)Ct^;~RR^%;&bX@f|8E;m``qn46x z63oXWSb=O!rXr9>{WQ6XM1XXW;N(*r-ZCINRx*4Bk_<)4K;jpNO>TZlX-=wLk@jRm K&iyP}KtTXqZAokZ delta 348 zcmZ1^{6&~|IWI340}!0^5Xrc;kynqAF>kXC<2^>!>>7pzyps)?L?+K>mYVF#Y{r$s zUIkQ$B&@;$?VeMaHQnyh;jxD%60GV}A|^HM8HZgIL+ zB&QaD_?m3Dm=g;MisUE%WNqeB1DeeT#KmVfx3TFn>TPgb>Dl4j@7v`&SK}g&@)Z!$ zyuriY@89Krkw^9lkL(2)dcea!`67Fzo~y7M8^cjaZ6JA!jmyo1`IvyDn?Ca~eO4e_ zlc@+4Hh!91MWR5uNN92ehqnyK)|CvOfh0qbDv1+{UcQ$yLKTn_(_f3g_e<%({%+ zlbfmG-)^RY5Aq%Z_eJS!#2QNxhM2r>`^W;3L4*03z#fT$8?m>Hp5(|6t2l#%({#`lXox&Fui1)%*tZSC@|TQWt9SVVsc4leqMZDYDLK{ zPS=X$)B+G+lkFCBVnIQXA diff --git a/maildir_gtd/actions/archive.py b/maildir_gtd/actions/archive.py index d84b75c..50038b5 100644 --- a/maildir_gtd/actions/archive.py +++ b/maildir_gtd/actions/archive.py @@ -28,8 +28,8 @@ async def archive_current(app) -> None: logging.info(stdout.decode()) if process.returncode == 0: await app.query_one(ListView).pop(index) - app.query_one(ListView).index = index + 1 - app.action_next() # Automatically show the next message + app.query_one(ListView).index = index + # app.action_next() # Automatically show the next message else: app.show_status(f"Error archiving message: {stderr.decode()}", "error") except Exception as e: diff --git a/maildir_gtd/actions/delete.py b/maildir_gtd/actions/delete.py index 8eae076..54d6047 100644 --- a/maildir_gtd/actions/delete.py +++ b/maildir_gtd/actions/delete.py @@ -17,8 +17,8 @@ async def delete_current(app) -> None: app.show_status(f"{stdout.decode()}", "info") if process.returncode == 0: await app.query_one(ListView).pop(index) - app.query_one(ListView).index = index + 1 - app.action_next() # Automatically show the next message + app.query_one(ListView).index = index + # app.action_next() # Automatically show the next message else: app.show_status(f"Failed to delete message {app.current_message_id}. {stderr.decode()}", "error") except Exception as e: diff --git a/maildir_gtd/app.py b/maildir_gtd/app.py index 818a35d..57157cd 100644 --- a/maildir_gtd/app.py +++ b/maildir_gtd/app.py @@ -57,12 +57,10 @@ class EmailViewerApp(App): header_expanded = reactive(False) reload_needed = reactive(True) all_envelopes = reactive([]) - next_id: Reactive[int] = reactive(0) - previous_id: Reactive[int] = reactive(0) oldest_id: Reactive[int] = reactive(0) newest_id: Reactive[int] = reactive(0) msg_worker: Worker | None = None - messsage_metadata: dict[int, dict] = {} + message_metadata: dict[int, dict] = {} message_body_cache: dict[int, str] = {} total_messages: Reactive[int] = reactive(0) status_title = reactive("Message View") @@ -156,36 +154,28 @@ class EmailViewerApp(App): self.query_one("#envelopes_list").border_title = f"\[1] Emails {sort_indicator}" def watch_current_message_index(self, old_index: int, new_index: int) -> None: - self.query_one("#envelopes_list").border_subtitle = f"[b]{self.current_message_index}[/b]/{self.total_messages}" + if (new_index < 0): + new_index = 0 + self.current_message_index = new_index + if (new_index > self.total_messages): + new_index = self.total_messages + self.current_message_index = new_index + self.query_one("#envelopes_list").border_subtitle = f"[b]{new_index}[/b]/{self.total_messages}" + self.query_one("#envelopes_list").index = new_index + def compute_newest_id(self) -> None: if not self.all_envelopes: return 0 - return sorted((int(envelope['id']) for envelope in self.valid_envelopes))[-1] + items = sorted(self.valid_envelopes, key=lambda x: x['date'], reverse=not self.sort_order_ascending) + return items[-1]['id'] if items else 0 def compute_oldest_id(self) -> None: if not self.valid_envelopes: return 0 - sorted_msgs = sorted((int(envelope['id']) for envelope in self.valid_envelopes)) - if len(sorted_msgs) > 0: - return sorted_msgs[0] - return 0 + items = sorted(self.valid_envelopes, key=lambda x: x['date'], reverse=not self.sort_order_ascending) + return items[0]['id'] if items else 0 - def compute_next_id(self) -> None: - if not self.valid_envelopes: - return 0 - for envelope_id in sorted(int(envelope['id']) for envelope in self.valid_envelopes): - if envelope_id > int(self.current_message_id): - return envelope_id - return self.newest_id - - def compute_previous_id(self) -> None: - if not self.valid_envelopes: - return 0 - for envelope_id in sorted((int(envelope['id']) for envelope in self.valid_envelopes), reverse=True): - if envelope_id < int(self.current_message_id): - return envelope_id - return self.oldest_id def watch_reload_needed(self, old_reload_needed: bool, new_reload_needed: bool) -> None: logging.info(f"Reload needed: {new_reload_needed}") @@ -200,14 +190,17 @@ class EmailViewerApp(App): return self.msg_worker.cancel() if self.msg_worker else None headers = self.query_one(EnvelopeHeader) - + logging.info(f"new_message_id: {new_message_id}, type: {type(new_message_id)}") + logging.info(f"message_metadata keys: {list(self.message_metadata.keys())}") if new_message_id in self.message_metadata: metadata = self.message_metadata[new_message_id] self.current_message_index = metadata['index'] headers.subject = metadata['subject'].strip() headers.from_ = metadata['from'].get('addr', '') headers.to = metadata['to'].get('addr', '') - headers.date = datetime.strptime(metadata['date'].replace("+00:00", ""), "%Y-%m-%d %H:%M").strftime("%a %b %d %H:%M") + msgdate = re.sub(r"[\+\-]\d\d:\d\d", "", metadata['date']) + msgdate = datetime.strptime(msgdate, "%Y-%m-%d %H:%M").strftime("%a %b %d %H:%M") + headers.date = msgdate headers.cc = metadata['cc'].get('addr', '') if 'cc' in metadata else "" self.query_one(ListView).index = metadata['index'] else: @@ -277,11 +270,11 @@ class EmailViewerApp(App): self.total_messages = len(envelopes) msglist.clear() - envelopes = sorted(envelopes, key=lambda x: int(x['id']), reverse=not self.sort_order_ascending) + envelopes = sorted(envelopes, key=lambda x: x['date'], reverse=not self.sort_order_ascending) grouped_envelopes = group_envelopes_by_date(envelopes) self.all_envelopes = grouped_envelopes self.message_metadata = { - envelope['id']: { + int(envelope['id']): { 'subject': envelope.get('subject', ''), 'from': envelope.get('from', {}), 'to': envelope.get('to', {}), @@ -355,12 +348,14 @@ class EmailViewerApp(App): finally: folders_list.loading = False - def show_message(self, message_id: int) -> None: + def show_message(self, message_id: int, new_index=None) -> None: + if new_index: + self.current_message_index = new_index self.current_message_id = 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.6, markup=True) + self.notify(message, title="Status", severity=severity, timeout=2.6, markup=True) def action_toggle_header(self) -> None: """Toggle the visibility of the EnvelopeHeader panel.""" @@ -381,30 +376,42 @@ class EmailViewerApp(App): self.action_newest() def action_next(self) -> None: - if self.sort_order_ascending: - self.show_message(self.next_id) - else: - self.show_message(self.previous_id) + if not self.current_message_index >= 0: + return + modifier = 1 + idx = self.current_message_index + if self.all_envelopes[idx + modifier] is None or self.all_envelopes[idx + modifier].get("type") == "header": + idx = idx + modifier + self.show_message(self.all_envelopes[idx + modifier].get("id"), idx + modifier) self.fetch_envelopes() if self.reload_needed else None def action_previous(self) -> None: - if self.sort_order_ascending: - self.show_message(self.previous_id) - else: - self.show_message(self.next_id) + if not self.current_message_index >= 0: + return + modifier = -1 + idx = self.current_message_index + if self.all_envelopes[idx + modifier] is None or self.all_envelopes[idx + modifier].get("type") == "header": + idx = idx + modifier + self.show_message(self.all_envelopes[idx + modifier].get("id"), idx + modifier) self.fetch_envelopes() if self.reload_needed else None - def action_delete(self) -> None: + async def action_delete(self) -> None: self.all_envelopes.remove(self.all_envelopes[self.current_message_index]) self.message_body_cache.pop(self.current_message_id, None) self.total_messages = len(self.all_envelopes) delete_current(self) - def action_archive(self) -> None: + async def action_archive(self) -> None: self.all_envelopes.remove(self.all_envelopes[self.current_message_index]) self.message_body_cache.pop(self.current_message_id, None) self.total_messages = len(self.all_envelopes) - archive_current(self) + worker = archive_current(self) + await worker.wait() + newmsg = self.all_envelopes[self.current_message_index] + if newmsg.get('type') == "header": + newmsg = self.current_message_index = self.current_message_index + 1 + return + self.show_message(newmsg['id']) def action_open(self) -> None: action_open(self) diff --git a/maildir_gtd/email_viewer.tcss b/maildir_gtd/email_viewer.tcss index 34d47d0..83b6a9c 100644 --- a/maildir_gtd/email_viewer.tcss +++ b/maildir_gtd/email_viewer.tcss @@ -140,7 +140,7 @@ Markdown { } Label.group_header { - color: rgb(64,64,64); + color: rgb(140, 140, 140); text-style: bold; background: rgb(64, 62, 65); width: 100%; diff --git a/maildir_gtd/utils.py b/maildir_gtd/utils.py index dd1b2d5..6c86541 100644 --- a/maildir_gtd/utils.py +++ b/maildir_gtd/utils.py @@ -1,4 +1,5 @@ from datetime import datetime, timedelta +import re from typing import List, Dict def group_envelopes_by_date(envelopes: List[Dict]) -> List[Dict]: @@ -29,7 +30,8 @@ def group_envelopes_by_date(envelopes: List[Dict]) -> List[Dict]: current_group = None for envelope in envelopes: - envelope_date = datetime.strptime(envelope['date'].split('+')[0], "%Y-%m-%d %H:%M") + envelope_date = re.sub(r"[\+\-]\d\d:\d\d", "", envelope['date']) + envelope_date = datetime.strptime(envelope_date, "%Y-%m-%d %H:%M") group_label = get_group_label(envelope_date) if group_label != current_group: grouped_envelopes.append({"type": "header", "label": group_label})