tangled
alpha
login
or
join now
aly.codes
/
atbbs
0
fork
atom
Retro Bulletin Board Systems on atproto. Web app and TUI.
atbbs.xyz
python
tui
atproto
bbs
0
fork
atom
overview
issues
pulls
pipelines
tui: properly paginate replies
Aly Raffauf
2 days ago
53dfbb19
69be5e2f
+42
-21
2 changed files
expand all
collapse all
unified
split
tui
app.tcss
screens
thread.py
+6
tui/app.tcss
reviewed
···
108
108
padding: 1 4;
109
109
}
110
110
111
111
+
.page-status {
112
112
+
color: #8a8a8a;
113
113
+
text-align: center;
114
114
+
margin: 1 0;
115
115
+
}
116
116
+
111
117
/* home screen */
112
118
HomeScreen {
113
119
align: center middle;
+36
-21
tui/screens/thread.py
reviewed
···
3
3
from textual.binding import Binding
4
4
from textual.containers import VerticalScroll
5
5
from textual.screen import Screen
6
6
-
from textual.widgets import Button, Footer
6
6
+
from textual.widgets import Footer, Static
7
7
8
8
from core import lexicon
9
9
from core.models import AtUri, BBS, Thread
···
17
17
Binding("escape", "app.pop_screen", "back"),
18
18
Binding("ctrl+e", "reply", "reply"),
19
19
Binding("ctrl+d", "delete", "delete"),
20
20
+
Binding("[", "prev_page", "prev page"),
21
21
+
Binding("]", "next_page", "next page"),
20
22
Binding("ctrl+s", "save_attachment", "save attachments", show=False),
21
23
]
22
24
···
54
56
collection=lexicon.THREAD,
55
57
attachments=self.thread.attachments,
56
58
)
59
59
+
yield Static("", id="page-status-top", classes="page-status")
60
60
+
yield Static("", id="page-status-bottom", classes="page-status")
57
61
yield Footer()
58
62
59
63
def on_mount(self) -> None:
···
63
67
pass
64
68
self.load_replies()
65
69
70
70
+
def _update_page_status(self) -> None:
71
71
+
text = f"page {self._page} of {self._total_pages}" if self._total_pages > 1 else ""
72
72
+
self.query_one("#page-status-top", Static).update(text)
73
73
+
self.query_one("#page-status-bottom", Static).update(text)
74
74
+
75
75
+
def _clear_replies(self) -> None:
76
76
+
for post in self.query(Post):
77
77
+
if post.collection == lexicon.REPLY:
78
78
+
post.remove()
79
79
+
self._replies_map.clear()
80
80
+
66
81
@work(exclusive=True)
67
82
async def load_replies(self, page: int = 1) -> None:
68
83
client = self.app.http_client
···
79
94
80
95
self._page = result.page
81
96
self._total_pages = result.total_pages
97
97
+
self._update_page_status()
98
98
+
82
99
scroll = self.query_one("#thread-scroll")
83
100
84
84
-
# Store for quote lookup
85
101
for r in result.replies:
86
102
self._replies_map[r.uri] = r
87
103
···
103
119
collection=lexicon.REPLY,
104
120
attachments=r.attachments,
105
121
quote_text=quote_text,
106
106
-
)
122
122
+
),
123
123
+
before=self.query_one("#page-status-bottom"),
107
124
)
108
125
109
109
-
if self._page < self._total_pages:
110
110
-
await scroll.mount(Button(f"page {self._page + 1} of {self._total_pages} →", id="next-page"))
111
111
-
112
112
-
def refresh_data(self) -> None:
113
113
-
self._do_refresh()
114
114
-
115
115
-
@work(exclusive=True)
116
116
-
async def _do_refresh(self) -> None:
117
117
-
for post in self.query(Post):
118
118
-
if post.collection == lexicon.REPLY:
119
119
-
await post.remove()
126
126
+
# Focus first reply
120
127
try:
121
121
-
await self.query_one("#next-page", Button).remove()
128
128
+
replies = [p for p in self.query(Post) if p.collection == lexicon.REPLY]
129
129
+
if replies:
130
130
+
replies[0].focus()
122
131
except Exception:
123
132
pass
124
133
125
125
-
self._replies_map.clear()
134
134
+
def action_next_page(self) -> None:
135
135
+
if self._page < self._total_pages:
136
136
+
self._clear_replies()
137
137
+
self.load_replies(page=self._page + 1)
138
138
+
139
139
+
def action_prev_page(self) -> None:
140
140
+
if self._page > 1:
141
141
+
self._clear_replies()
142
142
+
self.load_replies(page=self._page - 1)
143
143
+
144
144
+
def refresh_data(self) -> None:
145
145
+
self._clear_replies()
126
146
self._page = 1
127
147
self.load_replies(page=1)
128
128
-
129
129
-
def on_button_pressed(self, event: Button.Pressed) -> None:
130
130
-
if event.button.id == "next-page" and self._page < self._total_pages:
131
131
-
event.button.remove()
132
132
-
self.load_replies(page=self._page + 1)
133
148
134
149
def action_reply(self) -> None:
135
150
session = require_session(self)