we have a very basic search system working
This commit is contained in:
6
banner.txt
Normal file
6
banner.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
______ _ _______ __ __ __
|
||||
/_ __/_ __(_) ____(_) /_/ /_ __ __/ /_
|
||||
/ / / / / / / / __/ / __/ __ \/ / / / __ \
|
||||
/ / / /_/ / / /_/ / / /_/ / / / /_/ / /_/ /
|
||||
/_/ \__,_/_/\____/_/\__/_/ /_/\__,_/_.___/
|
||||
15
main.py
Normal file
15
main.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from screens.welcome_screen import WelcomeScreen
|
||||
from screens.search_screen import SearchScreen
|
||||
|
||||
|
||||
class TuiGithub(App):
|
||||
GITEA_HOST = "https://chookspace.com/"
|
||||
|
||||
def on_compose(self):
|
||||
self.push_screen(SearchScreen())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = TuiGithub()
|
||||
app.run()
|
||||
123
screens/search_screen.py
Normal file
123
screens/search_screen.py
Normal file
@@ -0,0 +1,123 @@
|
||||
from widgets import Navbar
|
||||
from textual.screen import Screen
|
||||
from textual.widgets import Input, Select, LoadingIndicator, Static
|
||||
from textual.containers import HorizontalGroup, ScrollableContainer
|
||||
from textual.app import ComposeResult
|
||||
|
||||
from human_readable import time_delta
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
class SearchResult(HorizontalGroup):
|
||||
DEFAULT_CSS = """
|
||||
SearchResult {
|
||||
padding: 0 1;
|
||||
margin: 0 1;
|
||||
margin-top: 1;
|
||||
border: tall $surface;
|
||||
|
||||
Static {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
author: str,
|
||||
name: str,
|
||||
description: str,
|
||||
is_fork: bool,
|
||||
updated_at: datetime
|
||||
):
|
||||
super().__init__()
|
||||
self.author = author
|
||||
self.repo_name = name
|
||||
self.description = description
|
||||
self.is_fork = is_fork
|
||||
self.updated_at = updated_at.replace(tzinfo=None)
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
updated_string = time_delta(datetime.now() - self.updated_at)
|
||||
yield Static(f"[b]{self.author}[/] / [b]{self.repo_name}[/]{' [d]\[[blue]fork[/]]' if self.is_fork else ''}\n{self.description}\n[d]Updated {updated_string} ago")
|
||||
|
||||
class SearchScreen(Screen):
|
||||
DEFAULT_CSS = """
|
||||
#search-box {
|
||||
padding: 1;
|
||||
padding-bottom: 0;
|
||||
border-bottom: hkey $surface;
|
||||
|
||||
#query {
|
||||
width: 1fr;
|
||||
}
|
||||
|
||||
#search-select {
|
||||
width: 25;
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
def action_search_query(self):
|
||||
query_input = self.query_one("#query")
|
||||
loading = self.query_one(LoadingIndicator)
|
||||
results = self.query_one(ScrollableContainer)
|
||||
|
||||
loading.display = True
|
||||
|
||||
# send off a request
|
||||
response = requests.get(
|
||||
url=self.app.GITEA_HOST + "api/v1/repos/search",
|
||||
params={
|
||||
"q": query_input.value,
|
||||
"limit": 20
|
||||
}
|
||||
)
|
||||
|
||||
loading.display = False
|
||||
|
||||
# error handling
|
||||
if not response.ok:
|
||||
self.notify(response.text, title="Error while getting search results:", severity="error")
|
||||
return
|
||||
|
||||
# display results
|
||||
results.remove_children(SearchResult)
|
||||
to_mount = []
|
||||
print(response.json())
|
||||
for result in response.json()["data"]:
|
||||
to_mount.append(SearchResult(
|
||||
result["owner"]["login"],
|
||||
result["name"],
|
||||
result["description"],
|
||||
result["fork"],
|
||||
datetime.fromisoformat(result["updated_at"])
|
||||
))
|
||||
results.mount_all(to_mount)
|
||||
|
||||
# self explanitory
|
||||
query_input.clear()
|
||||
|
||||
def on_input_submitted(self, event: Input.Submitted):
|
||||
if event.input.id == "query":
|
||||
self.action_search_query()
|
||||
|
||||
def on_mount(self):
|
||||
self.action_search_query()
|
||||
#self.query_one(LoadingIndicator).display = False
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Navbar()
|
||||
|
||||
with HorizontalGroup(id="search-box"):
|
||||
yield Input(placeholder="Search repos...", id="query")
|
||||
yield Select.from_values([
|
||||
"Repositories",
|
||||
], id="search-select", allow_blank=False)
|
||||
|
||||
with ScrollableContainer(id="results"):
|
||||
yield LoadingIndicator()
|
||||
47
screens/welcome_screen.py
Normal file
47
screens/welcome_screen.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from textual.containers import Vertical, HorizontalGroup, Center
|
||||
from textual.screen import Screen
|
||||
from textual.app import ComposeResult
|
||||
from textual.widgets import Static, Button
|
||||
from textualeffects.widgets import EffectLabel, EffectType, effects
|
||||
|
||||
from widgets import Navbar
|
||||
|
||||
from random import choice
|
||||
|
||||
|
||||
class WelcomeScreen(Screen):
|
||||
DEFAULT_CSS = """
|
||||
Center {
|
||||
padding: 2;
|
||||
width: 100%;
|
||||
|
||||
Static {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
EffectLabel {
|
||||
text-style: bold;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
#buttons {
|
||||
margin-top: 1;
|
||||
align: center middle;
|
||||
Button {
|
||||
margin: 0 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Navbar()
|
||||
with Center():
|
||||
with open("banner.txt", "r") as f:
|
||||
yield EffectLabel(text=f.read(), effect=choice(list(effects.keys())))
|
||||
|
||||
yield Static("[d]Gitea, in your terminal.[/]")
|
||||
|
||||
with HorizontalGroup(id="buttons"):
|
||||
yield Button("Explore", variant="primary", flat=True)
|
||||
yield Button("another button lol", flat=True)
|
||||
1
widgets/__init__.py
Normal file
1
widgets/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .navbar import Navbar
|
||||
21
widgets/navbar.py
Normal file
21
widgets/navbar.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from textual.widgets import Static, Button
|
||||
from textual.containers import HorizontalGroup
|
||||
|
||||
|
||||
class Navbar(HorizontalGroup):
|
||||
DEFAULT_CSS = """
|
||||
Navbar {
|
||||
background: $surface-darken-1;
|
||||
border-bottom: hkey $surface;
|
||||
height: 3;
|
||||
padding: 1;
|
||||
padding-bottom: 0;
|
||||
|
||||
#hamburger-menu {
|
||||
max-width: 1;
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
def compose(self):
|
||||
yield Button("≡", compact=True, id="hamburger-menu")
|
||||
Reference in New Issue
Block a user