From 906566825f7b628ad4991f05562435abefa7ea21 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Wed, 30 Apr 2025 12:26:35 +0200 Subject: [PATCH] TUI --- requirements.txt | 2 + tui.py | 119 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 tui.py diff --git a/requirements.txt b/requirements.txt index 19d4264..03d29b3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,5 @@ Flask~=1.1.2 requests~=2.24.0 python-dotenv~=0.15.0 pathvalidate~=2.5.2 + +textual~=3.1.1 diff --git a/tui.py b/tui.py new file mode 100644 index 0000000..f91f343 --- /dev/null +++ b/tui.py @@ -0,0 +1,119 @@ +import sys + +from textual import work +from textual.app import App, ComposeResult +from textual.containers import Container +from textual.screen import Screen +from textual.widgets import Header, Button, ListView, ListItem, Footer, Label + +from exporter_ng import SlackExporter, SlackConfig + + +class SlackExporterScreen(Screen): + """Ekran główny eksportera Slack""" + + def __init__(self, exporter: SlackExporter, *args, **kwargs): + super().__init__(*args, **kwargs) + self.exporter = exporter + self.selected_channels = set() + + def compose(self) -> ComposeResult: + """Komponuje widgety na ekranie""" + yield Header(show_clock=True) + yield Container( + ListView(*[ + ListItem(Label(ch.short_label, id=ch.id)) + for ch in self.exporter.channels + ], id="channel-list"), + Button("Eksportuj zaznaczone", variant="primary", id="export-btn"), + id="main-container" + ) + yield Footer() + + def on_list_view_selected(self, event: ListView.Selected) -> None: + """Obsługuje zaznaczenie elementu listy""" + item = event.item + item_id = item.children[0].id + if item_id in self.selected_channels: + self.selected_channels.remove(item_id) + item.remove_class("selected") + else: + self.selected_channels.add(item_id) + item.add_class("selected") + + @work(exclusive=True) + async def export_channels(self): + """Eksportuje zaznaczone kanały""" + if not self.selected_channels: + self.notify("Nie wybrano żadnych kanałów") + return + + # self.notify("Rozpoczynam eksport...") + # self.exporter.export_channels(list(self.selected_channels)) + # # for channel_id in self.selected_channels: + # # self.notify(f"Eksportuję kanał {channel_id}...") + # # self.exporter.export_channel_history(channel_id) + # self.notify("Eksport zakończony") + self.app.exit(return_code=8080) + + + async def on_button_pressed(self, event: Button.Pressed) -> None: + """Obsługuje kliknięcie przycisku""" + if event.button.id == "export-btn": + self.export_channels() + +class SlackTUI(App): + """Główna klasa interfejsu użytkownika""" + CSS = """ + #main-container { + layout: vertical; + height: 100%; + padding: 1; + } + + ListView { + height: 1fr; + border: solid green; + } + + .selected { + background: $accent; + color: $text; + } + + Button { + margin: 1; + width: 100%; + } + """ + + def __init__(self, exporter: SlackExporter): + super().__init__() + self.exporter = exporter + self._screen = SlackExporterScreen(self.exporter) + + def on_mount(self) -> None: + """Wywoływane przy montowaniu aplikacji""" + self.push_screen(self._screen) + + def get_selection(self): + return self._screen.selected_channels + + def get_return_code(self): + return self._return_code + +def run_tui(exporter: SlackExporter): + """Uruchamia interfejs użytkownika""" + app = SlackTUI(exporter) + app.run() + if app.get_return_code() == 8080: + exporter.export_channels(app.get_selection()) + +if __name__ == "__main__": + try: + config = SlackConfig.from_env() + exporter = SlackExporter(config) + run_tui(exporter) + except Exception as e: + print(f"Błąd: {e}") + sys.exit(1)