Skip to content

Commit 2a1bb07

Browse files
committed
TUI: add progress bar, mess with styling
1 parent d111e72 commit 2a1bb07

File tree

3 files changed

+94
-45
lines changed

3 files changed

+94
-45
lines changed

src/redfetch/sync.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,8 @@ async def sync(
371371
return False
372372

373373
print(f"Total resources to process: >>> {len(tasks)} <<<")
374+
if on_event:
375+
on_event(("total", len(tasks), None))
374376

375377
download_results: List[Tuple[str, str]] = []
376378
try:

src/redfetch/terminal_ui.py

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
from textual import work, on
2222
from textual.app import App, ComposeResult, SystemCommand
2323
from textual.binding import Binding
24-
from textual.widgets import Footer, Button, Header, Label, Input, Switch, Select, TabbedContent, TabPane, Log
24+
from textual.widgets import Footer, Button, Header, Label, Input, Switch, Select, TabbedContent, TabPane, Log, Static, ProgressBar
2525
from textual.events import Print
26-
from textual.containers import ScrollableContainer, Center, Grid, ItemGrid, Vertical
26+
from textual.containers import ScrollableContainer, Center, CenterMiddle, Grid, ItemGrid, Vertical
2727
from textual.reactive import reactive
2828
from textual.worker import Worker, WorkerState
2929
from textual.screen import ModalScreen, Screen
@@ -55,15 +55,29 @@ def compose(self) -> ComposeResult:
5555
# Simple vertical layout: controls on top, big log on the bottom
5656
with Vertical(id="fetch_layout"):
5757
with Grid(id="fetch_grid"):
58-
with Center(id="center_welcome"):
59-
yield Label("Who's this?", id="welcome_label")
60-
with Center(id="center_watched"):
61-
yield Button(
62-
"Checking if Very Vanilla MQ is up. 🍦",
63-
id="update_watched",
64-
variant="default",
65-
tooltip="is MQ down?",
66-
)
58+
yield Select[str](
59+
[("Live", "LIVE"), ("Test", "TEST"), ("Emu", "EMU")],
60+
id="server_type_fetch",
61+
classes="bordertitles",
62+
value=current_env,
63+
prompt="Select server type",
64+
allow_blank=False,
65+
tooltip=(
66+
"The type of EQ server. Live and Test are official servers, "
67+
"while Emu is for unofficial servers."
68+
),
69+
)
70+
with CenterMiddle(id="centermiddle_welcome"):
71+
with Center(id="center_welcome"):
72+
yield Label("Who's this?", id="welcome_label")
73+
with Center(id="center_watched"):
74+
yield Button(
75+
"Checking if Very Vanilla MQ is up. 🍦",
76+
id="update_watched",
77+
variant="default",
78+
tooltip="is MQ down?",
79+
)
80+
yield Static("", id="spacer_for_welcome_centering")
6781
yield Button(
6882
"Update Single Resource",
6983
id="update_resource_id",
@@ -76,16 +90,7 @@ def compose(self) -> ComposeResult:
7690
id="resource_id_input",
7791
tooltip="Update a single resource by its ID or URL.",
7892
)
79-
yield Select[str](
80-
[("Live", "LIVE"), ("Test", "TEST"), ("Emu", "EMU")],
81-
id="server_type_fetch",
82-
value=current_env,
83-
allow_blank=False,
84-
tooltip=(
85-
"The type of EQ server. Live and Test are official servers, "
86-
"while Emu is for unofficial servers."
87-
),
88-
)
93+
yield ProgressBar(total=None, show_eta=True, id="update_progress", classes="hidden")
8994
with Vertical(id="fetch_log_container"):
9095
# Toolbar row with log actions
9196
with Grid(id="log_toolbar"):
@@ -994,6 +999,7 @@ def on_mount(self) -> None:
994999

9951000
# Set border titles
9961001
self.query_one("#server_type").border_title = "Server type"
1002+
self.query_one("#server_type_fetch").border_title = "Server type"
9971003
self.query_one("#inputs_grid").border_title = "Directories"
9981004
self.query_one("#settings_grid").border_title = "Settings"
9991005
self.query_one("#special_resources_grid").border_title = "Special Resources"
@@ -1750,6 +1756,34 @@ def cancel_update_watched(self):
17501756
if cancelled_workers:
17511757
self.notify("Update canceled.", severity="warning")
17521758

1759+
def on_sync_event(self, event: tuple) -> None:
1760+
"""Handle events from the sync process to update the UI."""
1761+
event_type, resource_id, details = event
1762+
self._process_sync_event(event_type, resource_id, details)
1763+
1764+
def _process_sync_event(self, event_type: str, resource_id: str | int, details: str | None) -> None:
1765+
"""Process sync events on the main thread."""
1766+
main_screen = self._get_main_screen()
1767+
if not main_screen:
1768+
return
1769+
1770+
try:
1771+
fetch_tab = main_screen.query_one(FetchTab)
1772+
progress_bar = fetch_tab.query_one("#update_progress", ProgressBar)
1773+
resource_input = fetch_tab.query_one("#resource_id_input", Input)
1774+
1775+
if event_type == "total":
1776+
total_tasks = int(resource_id)
1777+
if total_tasks > 0:
1778+
progress_bar.total = total_tasks
1779+
progress_bar.progress = 0
1780+
progress_bar.remove_class("hidden")
1781+
resource_input.add_class("hidden")
1782+
elif event_type == "done":
1783+
progress_bar.advance(1)
1784+
except Exception:
1785+
pass
1786+
17531787
async def run_synchronization(self, resource_ids=None):
17541788
try:
17551789
db_name = f"{self.current_env}_resources.db"
@@ -1762,14 +1796,25 @@ async def run_synchronization(self, resource_ids=None):
17621796
)
17631797
if not reset_success:
17641798
return False
1765-
result = await sync.run_sync(db_path, headers, resource_ids=resource_ids)
1799+
result = await sync.run_sync(db_path, headers, resource_ids=resource_ids, on_event=self.on_sync_event)
17661800
return result
17671801
except Exception as e:
17681802
print(f"Error in run_synchronization: {e}")
17691803
return False
17701804

17711805
def update_complete(self, result: bool, button: Button) -> None:
17721806
main_screen = self._get_main_screen()
1807+
1808+
if main_screen:
1809+
try:
1810+
fetch_tab = main_screen.query_one(FetchTab)
1811+
progress_bar = fetch_tab.query_one("#update_progress", ProgressBar)
1812+
resource_input = fetch_tab.query_one("#resource_id_input", Input)
1813+
progress_bar.add_class("hidden")
1814+
resource_input.remove_class("hidden")
1815+
except Exception:
1816+
pass
1817+
17731818
if result:
17741819
button.variant = "success"
17751820
self.notify("All resources updated successfully.")

src/redfetch/terminal_ui.tcss

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Header {
1919
TabbedContent {
2020
padding: 1 1;
2121
}
22+
2223
TabPane {
2324
padding: 1 1;
2425
height: 1fr;
@@ -66,50 +67,49 @@ Label {
6667
layout: grid;
6768
grid-rows: auto 1fr;
6869
/* Match row spacing inside the fetch grid */
69-
grid-gutter: 1 0;
70+
grid-gutter: 1;
7071
min-height: 20;
72+
height: auto;
7173
}
7274

7375
#fetch_grid {
7476
layout: grid;
7577
grid-size: 3;
7678
grid-rows: auto;
77-
grid-gutter: 1;
79+
grid-gutter: 1 0;
7880
min-width: 55;
81+
height: auto;
7982
}
80-
#center_welcome {
81-
column-span: 3;
82-
}
83-
#welcome_label {
83+
84+
#server_type_fetch {
8485
content-align: center middle;
86+
width: 25;
87+
min-width: 15;
88+
column-span: 1;
8589
}
86-
#center_watched {
87-
column-span: 3;
90+
91+
#centermiddle_welcome {
92+
height: auto;
8893
}
94+
8995
#update_watched {
90-
content-align: center middle;
9196
color: $text;
9297
text-style: bold;
9398
padding: 1 1;
9499
}
100+
95101
#update_resource_id {
96-
margin: 0 1;
97102
min-width: 18;
103+
width: 100%;
104+
column-span: 1;
105+
margin-right: 0;
98106
}
99-
#resource_id_input {
100-
margin: 0 1 0 0;
107+
108+
#resource_id_input, #update_progress {
101109
min-width: 20;
102-
}
103-
#server_type_fetch {
104-
content-align: center middle;
105-
width: 26;
106-
min-width: 15;
107-
}
108-
#resource_id_input {
110+
width: 100%;
109111
column-span: 2;
110-
}
111-
#update_resource_id {
112-
column-span: 1;
112+
margin-left: 2;
113113
}
114114

115115
#fetch_log_container {
@@ -163,12 +163,14 @@ Label {
163163
height: 1fr;
164164
overflow-y: auto;
165165
}
166+
166167
#server_type {
167-
column-span:4;
168168
content-align: center middle;
169169
width: 25;
170170
min-width: 15;
171+
column-span: 4;
171172
}
173+
172174
#inputs_grid {
173175
margin-top: 1;
174176
margin-bottom: 1;

0 commit comments

Comments
 (0)