Skip to content

Commit

Permalink
feature: [tui] 支持预约
Browse files Browse the repository at this point in the history
  • Loading branch information
YDX-2147483647 committed May 14, 2023
1 parent 6bbafa9 commit 71d624f
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 4 deletions.
21 changes: 21 additions & 0 deletions src/bitroom/tui.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
BookScreen {
align: center middle;
}

#book-dialog {
grid-size: 2;
grid-gutter: 1 2;
grid-rows: 1fr 3;
padding: 0 1;
width: 60;
height: 11;
border: thick $background 80%;
background: $surface;
}

#book-dialog > #question {
column-span: 2;
height: 1fr;
width: 1fr;
content-align: center middle;
}
70 changes: 66 additions & 4 deletions src/bitroom/tui.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
from more_itertools import chunked
from textual import on, work
from textual.app import App
from textual.widgets import Footer, Header, Input, OptionList
from textual.containers import Grid
from textual.screen import Screen
from textual.widgets import Button, Footer, Header, Input, Label, OptionList
from textual.widgets.option_list import Option
from textual.worker import get_current_worker

Expand All @@ -31,14 +33,19 @@
class RoomApp(App):
"""App to interact with RoomAPI"""

CSS_PATH = "tui.css"

BINDINGS = [
("d", "toggle_dark", "Dark/切换深色模式"),
("q", "quit", "Quit/退出"),
("r", "refresh_bookings", "Refresh/刷新数据"),
("enter", "book", "预约选中的房间"),
]

config: Config
bookings: list[Booking]
bookings_matched_indices: list[int]
"""Search result"""

def __init__(self) -> None:
super().__init__()
Expand All @@ -49,6 +56,7 @@ def __init__(self) -> None:

with Path("bookings.json").open(encoding="utf-8") as f:
self.bookings = list(map(Booking.from_dict, load(f)))
self.bookings_matched_indices = list(range(len(self.bookings)))

def compose(self) -> ComposeResult:
yield Header()
Expand Down Expand Up @@ -84,27 +92,32 @@ def _search(self, keyword: str) -> None:
self.log(f"Start searching for “{keyword}”…")

# todo: More advanced search
result = (b for b in self.bookings if all(k in str(b) for k in keyword.split()))
result = (
i
for i, b in enumerate(self.bookings)
if all(k in str(b) for k in keyword.split())
)

option_list = self.query_one("#bookings", OptionList)

if worker.is_cancelled:
self.log(f"The search for “{keyword}” finished, but had been cancelled.")
return

self.bookings_matched_indices = list(result)
option_list.clear_options()

# `option_list.add_option()` always refreshes the widget, which can be slow.
# https://github.com/Textualize/textual/blob/14850d54a3f5fed878cff1ce2f5da08503a02932/src/textual/widgets/_option_list.py#L511-L531
#
# Here we add 100 options, refresh, add another 100, refresh, and so on.
for bookings_group in chunked(result, 100):
for bookings_group in chunked(self.bookings_matched_indices, 100):
if worker.is_cancelled:
self.log(f"The search for “{keyword}” is cancelled halfway.")
return

for b in bookings_group:
content = option_list._make_content(str(b))
content = option_list._make_content(str(self.bookings[b]))
option_list._contents.append(content)
if isinstance(content, Option):
option_list._options.append(content)
Expand All @@ -118,6 +131,12 @@ async def action_refresh_bookings(self) -> None:
"""刷新 bookings 数据"""
self._refresh_bookings()

def on_option_list_option_selected(
self, message: OptionList.OptionSelected
) -> None:
booking = self.bookings[self.bookings_matched_indices[message.option_index]]
self.push_screen(BookScreen(booking, self.config))

@work(exclusive=True)
async def _refresh_bookings(self) -> None:
self.log("Start refreshing bookings…")
Expand All @@ -134,6 +153,49 @@ async def _refresh_bookings(self) -> None:
self._search(self.query_one("#search", Input).value)


class BookScreen(Screen):
booking: Booking
config: Config

def __init__(self, booking: Booking, config: Config) -> None:
super().__init__()

self.booking = booking
self.config = config

def compose(self) -> ComposeResult:
yield Grid(
Label(f"你确定要预约“{self.booking}”吗?", id="question"),
Button("确认", variant="success", id="confirm"),
Button("取消", variant="primary", id="cancel"),
id="book-dialog",
)

@on(Button.Pressed, "#confirm")
async def book(self, message: Button.Pressed) -> None:
self.log("Start booking…")

async with AsyncClient() as client:
await auth(client, self.config.username, self.config.password)
api = await RoomAPI.build(client)

await api.book(
self.booking,
# todo
tel="13806491023",
applicant="Boltzmann",
)

# todo: Visualize result
self.log("Booked successfully.")

self.app.pop_screen()

@on(Button.Pressed, "#cancel")
def cancel(self, message: Button.Pressed) -> None:
self.app.pop_screen()


if __name__ == "__main__":
app = RoomApp()
app.run()

0 comments on commit 71d624f

Please sign in to comment.