Add Goodreads series and group them on the about page
This commit is contained in:
1 parent
737700a9a8
commit
2318cdeaac
11 files changed
+651
-12
No files matched your search
@@ -0,0 +1,97 @@
|
||||
from rich.text import Text
|
||||
from textual.app import ComposeResult
|
||||
from textual.binding import Binding
|
||||
from textual.screen import Screen
|
||||
from textual.widgets import DataTable, Footer, Label
|
||||
|
||||
SKIP = "skip"
|
||||
CHOICE_STYLES = {"read": "green", "reading": "blue", "want-to-read": "magenta", SKIP: "dim"}
|
||||
CHOICE_ORDER = ["read", "reading", "want-to-read", SKIP]
|
||||
|
||||
|
||||
class SeriesPicker(Screen[dict[str, str] | None]):
|
||||
BINDINGS = [
|
||||
Binding("r", "choose('read')", "Read"),
|
||||
Binding("c", "choose('reading')", "Reading"),
|
||||
Binding("w", "choose('want-to-read')", "Want to read"),
|
||||
Binding("x", "choose('skip')", "Skip"),
|
||||
Binding("space", "cycle", "Cycle"),
|
||||
Binding("R", "choose_all('read')", "All read"),
|
||||
Binding("W", "choose_all('want-to-read')", "All want"),
|
||||
Binding("ctrl+s", "save", "Save"),
|
||||
Binding("escape", "cancel", "Cancel"),
|
||||
]
|
||||
|
||||
def __init__(self, series: dict, current_shelves: dict[str, str]):
|
||||
super().__init__()
|
||||
self.series = series
|
||||
self.choices = {entry["goodreadsId"]: current_shelves.get(entry["goodreadsId"], SKIP) for entry in series["books"]}
|
||||
self.already_listed = set(current_shelves)
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Label(f"{self.series['name']} [dim]{len(self.series['books'])} books, pick a shelf for each one[/dim]", classes="editor-title")
|
||||
yield DataTable(cursor_type="row", zebra_stripes=True, id="series-table")
|
||||
yield Label("", id="series-summary", classes="series-summary")
|
||||
yield Footer()
|
||||
|
||||
def on_mount(self):
|
||||
table = self.query_one(DataTable)
|
||||
table.add_column("Shelf", key="shelf")
|
||||
table.add_column("#", key="position")
|
||||
table.add_column("Title", key="title")
|
||||
table.add_column("Author", key="author")
|
||||
table.add_column("", key="listed")
|
||||
for entry in self.series["books"]:
|
||||
identifier = entry["goodreadsId"]
|
||||
listed = "in library" if identifier in self.already_listed else ""
|
||||
table.add_row(self.choice_text(identifier), entry["position"] or "-", entry["title"], ", ".join(entry["authors"]), listed, key=identifier)
|
||||
table.focus()
|
||||
self.update_summary()
|
||||
|
||||
def choice_text(self, identifier: str) -> Text:
|
||||
choice = self.choices[identifier]
|
||||
return Text(choice, style=CHOICE_STYLES[choice])
|
||||
|
||||
def update_summary(self):
|
||||
counts = {choice: list(self.choices.values()).count(choice) for choice in CHOICE_ORDER}
|
||||
parts = [f"[{CHOICE_STYLES[choice]}]{counts[choice]} {choice}[/]" for choice in CHOICE_ORDER]
|
||||
self.query_one("#series-summary", Label).update(" ".join(parts) + " [dim]ctrl+s to save[/dim]")
|
||||
|
||||
def selected_id(self) -> str | None:
|
||||
table = self.query_one(DataTable)
|
||||
if table.row_count == 0:
|
||||
return None
|
||||
row_key, _ = table.coordinate_to_cell_key(table.cursor_coordinate)
|
||||
return row_key.value
|
||||
|
||||
def set_choice(self, identifier: str, choice: str):
|
||||
self.choices[identifier] = choice
|
||||
self.query_one(DataTable).update_cell(identifier, "shelf", self.choice_text(identifier))
|
||||
|
||||
def action_choose(self, choice: str):
|
||||
identifier = self.selected_id()
|
||||
if not identifier:
|
||||
return
|
||||
self.set_choice(identifier, choice)
|
||||
self.update_summary()
|
||||
self.query_one(DataTable).action_cursor_down()
|
||||
|
||||
def action_cycle(self):
|
||||
identifier = self.selected_id()
|
||||
if not identifier:
|
||||
return
|
||||
next_choice = CHOICE_ORDER[(CHOICE_ORDER.index(self.choices[identifier]) + 1) % len(CHOICE_ORDER)]
|
||||
self.set_choice(identifier, next_choice)
|
||||
self.update_summary()
|
||||
|
||||
def action_choose_all(self, choice: str):
|
||||
for identifier in self.choices:
|
||||
self.set_choice(identifier, choice)
|
||||
self.update_summary()
|
||||
|
||||
def action_save(self):
|
||||
picked = {identifier: choice for identifier, choice in self.choices.items() if choice != SKIP}
|
||||
self.dismiss(picked)
|
||||
|
||||
def action_cancel(self):
|
||||
self.dismiss(None)
|
||||
Reference in new issue
Block a user