from __future__ import annotations from typing import Any, Dict, List, Optional from textual.app import ComposeResult from textual.containers import Container, Horizontal, ScrollableContainer, Vertical from textual.screen import ModalScreen from textual.widgets import Static, Button, Checkbox from textual import work from rich.text import Text class MatrixRoomPicker(ModalScreen[List[str]]): """Modal that lists Matrix rooms and returns selected defaults.""" CSS = """ MatrixRoomPicker { align: center middle; background: rgba(0, 0, 0, 0.45); } #matrix-room-picker { width: 70%; height: 70%; background: $panel; border: thick $primary; padding: 1; } #matrix-room-picker-hint { text-style: italic; color: $text-muted; margin-bottom: 1; } #matrix-room-scroll { height: 1fr; border: solid $surface; padding: 1; margin-bottom: 1; } #matrix-room-checklist { padding: 0; } .matrix-room-row { border-bottom: solid $surface; padding: 0.5 0; align: left middle; } .matrix-room-meta { padding-left: 1; content-align: left middle; } #matrix-room-actions { height: 3; align: right middle; } #matrix-room-status { height: 3; text-style: bold; } """ def __init__( self, config: Dict[str, Any], *, existing: Optional[List[str]] = None, rooms: Optional[List[Dict[str, Any]]] = None, ) -> None: super().__init__() self.config = config self._prefetched_rooms = rooms self._existing_ids = {str(r).strip() for r in (existing or []) if str(r).strip()} self._checkbox_map: Dict[str, str] = {} self._rooms: List[Dict[str, Any]] = [] self._status_widget: Optional[Static] = None self._checklist: Optional[Vertical] = None self._save_button: Optional[Button] = None def compose(self) -> ComposeResult: with Container(id="matrix-room-picker"): yield Static("Matrix Default Rooms", classes="section-title") yield Static( "Choose rooms to keep in the sharing defaults and autocomplete.", id="matrix-room-picker-hint", ) with ScrollableContainer(id="matrix-room-scroll"): yield Vertical(id="matrix-room-checklist") with Horizontal(id="matrix-room-actions"): yield Button("Cancel", variant="error", id="matrix-room-cancel") yield Button("Save defaults", variant="success", id="matrix-room-save") yield Static("Loading rooms...", id="matrix-room-status") def on_mount(self) -> None: self._status_widget = self.query_one("#matrix-room-status", Static) self._checklist = self.query_one("#matrix-room-checklist", Vertical) self._save_button = self.query_one("#matrix-room-save", Button) if self._save_button: self._save_button.disabled = True if self._prefetched_rooms is not None: self._apply_room_results(self._prefetched_rooms, None) else: if self._status_widget: self._set_status("Loading rooms...") self._load_rooms_background() def _set_status(self, text: str) -> None: if self._status_widget: self._status_widget.update(text) def _render_rooms(self, rooms: List[Dict[str, Any]]) -> None: if not self._checklist: return for child in list(self._checklist.children): child.remove() self._checkbox_map.clear() self._rooms = rooms if not rooms: self._set_status("Matrix returned no rooms.") if self._save_button: self._save_button.disabled = True return for idx, room in enumerate(rooms): room_id = str(room.get("room_id") or "").strip() name = str(room.get("name") or "").strip() checkbox_id = f"matrix-room-{idx}" checkbox = Checkbox( "", id=checkbox_id, value=bool(room_id and room_id in self._existing_ids), ) self._checkbox_map[checkbox_id] = room_id label = Text(name or room_id or "Matrix Room") label.stylize("bold") label.append("\n") label.append(room_id or "(no id)", style="dim") row = Horizontal(classes="matrix-room-row") self._checklist.mount(row) row.mount(checkbox) row.mount(Static(label, classes="matrix-room-meta")) self._set_status("Loaded rooms. Select one or more and save.") if self._save_button: self._save_button.disabled = False @work(thread=True) def _load_rooms_background(self) -> None: try: from Provider.matrix import Matrix provider = Matrix(self.config) rooms = provider.list_rooms() self.app.call_from_thread(self._apply_room_results, rooms, None) except Exception as exc: self.app.call_from_thread(self._apply_room_results, [], str(exc)) def _apply_room_results(self, rooms: List[Dict[str, Any]], error: Optional[str]) -> None: if error: self._set_status(f"Failed to load Matrix rooms: {error}") if self._save_button: self._save_button.disabled = True return self._render_rooms(rooms) def on_button_pressed(self, event: Button.Pressed) -> None: if event.button.id == "matrix-room-cancel": self.dismiss([]) elif event.button.id == "matrix-room-save": selected: List[str] = [] for checkbox_id, room_id in self._checkbox_map.items(): try: cb = self.query_one(f"#{checkbox_id}", Checkbox) if cb.value and room_id: selected.append(room_id) except Exception: pass self.dismiss(selected)