From 16dda47681352d7b2cc8ecb4eb1faba6c17e4f21 Mon Sep 17 00:00:00 2001 From: gizmo385 Date: Mon, 9 Sep 2024 15:33:16 -0600 Subject: [PATCH] Make the GithubClient a singleton This should make adding new screens and views easier since we won't have to thread the existing GithubClient through the entire UI This addresses #6 --- lazy_github/lib/github/auth.py | 3 +- lazy_github/lib/github/client.py | 43 +++++++++++++++++- lazy_github/lib/github/issues.py | 28 ++++++------ lazy_github/lib/github/pull_requests.py | 36 ++++++++------- lazy_github/lib/github/repositories.py | 13 +++--- lazy_github/ui/app.py | 12 +++-- lazy_github/ui/screens/auth.py | 4 +- lazy_github/ui/screens/edit_issue.py | 11 ++--- lazy_github/ui/screens/new_comment.py | 11 ++--- lazy_github/ui/screens/primary.py | 59 ++++++++++--------------- lazy_github/ui/screens/settings.py | 10 ++--- lazy_github/ui/widgets/conversations.py | 20 +++------ lazy_github/ui/widgets/issues.py | 19 +++----- lazy_github/ui/widgets/pull_requests.py | 24 +++++----- lazy_github/ui/widgets/repositories.py | 6 +-- 15 files changed, 146 insertions(+), 153 deletions(-) diff --git a/lazy_github/lib/github/auth.py b/lazy_github/lib/github/auth.py index f5f068b..e38da59 100644 --- a/lazy_github/lib/github/auth.py +++ b/lazy_github/lib/github/auth.py @@ -4,8 +4,7 @@ import httpx -from lazy_github.lib.constants import CONFIG_FOLDER -from lazy_github.lib.constants import DEVICE_CODE_GRANT_TYPE, LAZY_GITHUB_CLIENT_ID +from lazy_github.lib.constants import CONFIG_FOLDER, DEVICE_CODE_GRANT_TYPE, LAZY_GITHUB_CLIENT_ID # Auth and client globals _AUTHENTICATION_CACHE_LOCATION = CONFIG_FOLDER / "auth.text" diff --git a/lazy_github/lib/github/client.py b/lazy_github/lib/github/client.py index 284eefa..8925834 100644 --- a/lazy_github/lib/github/client.py +++ b/lazy_github/lib/github/client.py @@ -1,9 +1,13 @@ -from typing import Optional +from collections.abc import Coroutine +from typing import Any, Optional import hishel +from httpx import URL, Response +from httpx._types import HeaderTypes, QueryParamTypes from lazy_github.lib.config import Config from lazy_github.lib.constants import JSON_CONTENT_ACCEPT_TYPE +from lazy_github.lib.github.auth import token from lazy_github.models.github import User @@ -30,3 +34,40 @@ async def user(self) -> User: response = await self.get("/user", headers=self.headers_with_auth_accept()) self._user = User(**response.json()) return self._user + + +_GITHUB_CLIENT: GithubClient | None = None + + +def _get_client() -> GithubClient: + global _GITHUB_CLIENT + if not _GITHUB_CLIENT: + _GITHUB_CLIENT = GithubClient(Config.load_config(), token()) + return _GITHUB_CLIENT + + +def get( + url: URL | str, + headers: HeaderTypes | None = None, + params: QueryParamTypes | None = None, + follow_redirects: bool = True, +) -> Coroutine[Any, Any, Response]: + return _get_client().get(url, headers=headers, params=params, follow_redirects=follow_redirects) + + +def post(url: URL | str, json: Any | None = None, headers: HeaderTypes | None = None) -> Coroutine[Any, Any, Response]: + return _get_client().post(url, json=json, headers=headers) + + +def patch(url: URL | str, json: Any | None, headers: HeaderTypes | None = None) -> Coroutine[Any, Any, Response]: + return _get_client().patch(url, json=json, headers=headers) + + +def headers_with_auth_accept( + accept: str = JSON_CONTENT_ACCEPT_TYPE, cache_duration: Optional[int] = None +) -> dict[str, str]: + return _get_client().headers_with_auth_accept(accept, cache_duration) + + +async def user() -> User: + return await _get_client().user() diff --git a/lazy_github/lib/github/issues.py b/lazy_github/lib/github/issues.py index 19eca1b..207318f 100644 --- a/lazy_github/lib/github/issues.py +++ b/lazy_github/lib/github/issues.py @@ -1,7 +1,8 @@ from typing import TypedDict, Unpack +import lazy_github.lib.github.client as github +from lazy_github.lib.config import Config from lazy_github.lib.constants import IssueOwnerFilter, IssueStateFilter -from lazy_github.lib.github.client import GithubClient from lazy_github.models.github import Issue, IssueComment, PartialPullRequest, Repository @@ -10,17 +11,16 @@ class UpdateIssuePayload(TypedDict): body: str | None -async def list_issues( - client: GithubClient, repo: Repository, state: IssueStateFilter, owner: IssueOwnerFilter -) -> list[Issue]: +async def list_issues(repo: Repository, state: IssueStateFilter, owner: IssueOwnerFilter) -> list[Issue]: """Fetch issues (included pull requests) from the repo matching the state/owner filters""" query_params = {"state": str(state).lower()} if owner == IssueOwnerFilter.MINE: - user = await client.user() + user = await github.user() query_params["creator"] = user.login - headers = client.headers_with_auth_accept(cache_duration=client.config.cache.list_issues_ttl) - response = await client.get(f"/repos/{repo.owner.login}/{repo.name}/issues", headers=headers, params=query_params) + config = Config.load_config() + headers = github.headers_with_auth_accept(cache_duration=config.cache.list_issues_ttl) + response = await github.get(f"/repos/{repo.owner.login}/{repo.name}/issues", headers=headers, params=query_params) response.raise_for_status() result: list[Issue] = [] for issue in response.json(): @@ -31,25 +31,23 @@ async def list_issues( return result -async def get_comments(client: GithubClient, issue: Issue) -> list[IssueComment]: - response = await client.get(issue.comments_url, headers=client.headers_with_auth_accept()) +async def get_comments(issue: Issue) -> list[IssueComment]: + response = await github.get(issue.comments_url, headers=github.headers_with_auth_accept()) response.raise_for_status() return [IssueComment(**i) for i in response.json()] -async def create_comment(client: GithubClient, issue: Issue, comment_body: str) -> IssueComment: +async def create_comment(issue: Issue, comment_body: str) -> IssueComment: url = f"/repos/{issue.repo.owner.login}/{issue.repo.name}/issues/{issue.number}/comments" body = {"body": comment_body} - response = await client.post(url, json=body, headers=client.headers_with_auth_accept()) + response = await github.post(url, json=body, headers=github.headers_with_auth_accept()) response.raise_for_status() return IssueComment(**response.json()) -async def update_issue( - client: GithubClient, issue_to_update: Issue, **updated_fields: Unpack[UpdateIssuePayload] -) -> Issue: +async def update_issue(issue_to_update: Issue, **updated_fields: Unpack[UpdateIssuePayload]) -> Issue: repo = issue_to_update.repo url = f"/repos/{repo.owner.login}/{repo.name}/issues/{issue_to_update.number}" - response = await client.patch(url, json=updated_fields, headers=client.headers_with_auth_accept()) + response = await github.patch(url, json=updated_fields, headers=github.headers_with_auth_accept()) response.raise_for_status() return Issue(**response.json(), repo=repo) diff --git a/lazy_github/lib/github/pull_requests.py b/lazy_github/lib/github/pull_requests.py index dd90920..0fe84e2 100644 --- a/lazy_github/lib/github/pull_requests.py +++ b/lazy_github/lib/github/pull_requests.py @@ -1,5 +1,6 @@ +from lazy_github.lib.config import Config from lazy_github.lib.constants import DIFF_CONTENT_ACCEPT_TYPE -from lazy_github.lib.github.client import GithubClient +import lazy_github.lib.github.client as github from lazy_github.lib.github.issues import list_issues from lazy_github.models.github import ( FullPullRequest, @@ -11,55 +12,56 @@ ) -async def list_for_repo(client: GithubClient, repo: Repository) -> list[PartialPullRequest]: +async def list_for_repo(repo: Repository) -> list[PartialPullRequest]: """Lists the pull requests associated with the specified repo""" - state_filter = client.config.pull_requests.state_filter - owner_filter = client.config.pull_requests.owner_filter - issues = await list_issues(client, repo, state_filter, owner_filter) + config = Config.load_config() + state_filter = config.pull_requests.state_filter + owner_filter = config.pull_requests.owner_filter + issues = await list_issues(repo, state_filter, owner_filter) return [i for i in issues if isinstance(i, PartialPullRequest)] -async def get_full_pull_request(client: GithubClient, partial_pr: PartialPullRequest) -> FullPullRequest: +async def get_full_pull_request(partial_pr: PartialPullRequest) -> FullPullRequest: """Converts a partial pull request into a full pull request""" url = f"/repos/{partial_pr.repo.owner.login}/{partial_pr.repo.name}/pulls/{partial_pr.number}" - response = await client.get(url, headers=client.headers_with_auth_accept()) + response = await github.get(url, headers=github.headers_with_auth_accept()) response.raise_for_status() return FullPullRequest(**response.json(), repo=partial_pr.repo) -async def get_diff(client: GithubClient, pr: FullPullRequest) -> str: +async def get_diff(pr: FullPullRequest) -> str: """Fetches the raw diff for an individual pull request""" - headers = client.headers_with_auth_accept(DIFF_CONTENT_ACCEPT_TYPE) - response = await client.get(pr.diff_url, headers=headers, follow_redirects=True) + headers = github.headers_with_auth_accept(DIFF_CONTENT_ACCEPT_TYPE) + response = await github.get(pr.diff_url, headers=headers, follow_redirects=True) response.raise_for_status() return response.text -async def get_review_comments(client: GithubClient, pr: FullPullRequest, review: Review) -> list[ReviewComment]: +async def get_review_comments(pr: FullPullRequest, review: Review) -> list[ReviewComment]: url = f"/repos/{pr.repo.owner.login}/{pr.repo.name}/pulls/{pr.number}/reviews/{review.id}/comments" - response = await client.get(url, headers=client.headers_with_auth_accept()) + response = await github.get(url, headers=github.headers_with_auth_accept()) response.raise_for_status() return [ReviewComment(**c) for c in response.json()] -async def get_reviews(client: GithubClient, pr: FullPullRequest, with_comments: bool = True) -> list[Review]: +async def get_reviews(pr: FullPullRequest, with_comments: bool = True) -> list[Review]: url = url = f"/repos/{pr.repo.owner.login}/{pr.repo.name}/pulls/{pr.number}/reviews" - response = await client.get(url, headers=client.headers_with_auth_accept()) + response = await github.get(url, headers=github.headers_with_auth_accept()) response.raise_for_status() reviews: list[Review] = [] for raw_review in response.json(): review = Review(**raw_review) if with_comments: - review.comments = await get_review_comments(client, pr, review) + review.comments = await get_review_comments(pr, review) reviews.append(review) return reviews async def reply_to_review_comment( - client: GithubClient, repo: Repository, issue: Issue, comment: ReviewComment, comment_body: str + repo: Repository, issue: Issue, comment: ReviewComment, comment_body: str ) -> ReviewComment: url = f"/repos/{repo.owner.login}/{repo.name}/pulls/{issue.number}/comments/{comment.id}/replies" - response = await client.post(url, headers=client.headers_with_auth_accept(), json={"body": comment_body}) + response = await github.post(url, headers=github.headers_with_auth_accept(), json={"body": comment_body}) response.raise_for_status() return ReviewComment(**response.json()) diff --git a/lazy_github/lib/github/repositories.py b/lazy_github/lib/github/repositories.py index 6819b7b..9b85470 100644 --- a/lazy_github/lib/github/repositories.py +++ b/lazy_github/lib/github/repositories.py @@ -1,7 +1,8 @@ from functools import partial from typing import Literal -from lazy_github.lib.github.client import GithubClient +import lazy_github.lib.github.client as github +from lazy_github.lib.config import Config from lazy_github.models.github import Repository RepoTypeFilter = Literal["all"] | Literal["owner"] | Literal["member"] @@ -13,7 +14,6 @@ async def _list_for_page( - client: GithubClient, repo_types: RepoTypeFilter, sort: RepositorySortKey, direction: SortDirection, @@ -21,9 +21,11 @@ async def _list_for_page( page: int, ) -> tuple[list[Repository], bool]: """Retrieves a single page of Github repos matching the specified criteria""" - headers = client.headers_with_auth_accept(cache_duration=client.config.cache.list_repos_ttl) + config = Config.load_config() + headers = github.headers_with_auth_accept(cache_duration=config.cache.list_repos_ttl) query_params = {"type": repo_types, "direction": direction, "sort": sort, "page": page, "per_page": per_page} - response = await client.get("/user/repos", headers=headers, params=query_params) + + response = await github.get("/user/repos", headers=headers, params=query_params) response.raise_for_status() link_header = response.headers.get("link") @@ -33,7 +35,6 @@ async def _list_for_page( async def _list( - client: GithubClient, repo_types: RepoTypeFilter, sort: RepositorySortKey = "full_name", direction: SortDirection = "asc", @@ -43,7 +44,7 @@ async def _list( repositories: list[Repository] = [] for page in range(1, MAX_PAGES): - repos_in_page, more_pages = await _list_for_page(client, repo_types, sort, direction, per_page, page) + repos_in_page, more_pages = await _list_for_page(repo_types, sort, direction, per_page, page) repositories.extend(repos_in_page) if not more_pages: break diff --git a/lazy_github/ui/app.py b/lazy_github/ui/app.py index f49103d..b2b5ee9 100644 --- a/lazy_github/ui/app.py +++ b/lazy_github/ui/app.py @@ -1,9 +1,8 @@ from textual import log from textual.app import App -from lazy_github.lib.config import Config -from lazy_github.lib.github.auth import GithubAuthenticationRequired, token -from lazy_github.lib.github.client import GithubClient +from lazy_github.lib.github.auth import GithubAuthenticationRequired +import lazy_github.lib.github.client as github from lazy_github.ui.screens.auth import AuthenticationModal from lazy_github.ui.screens.primary import LazyGithubMainScreen @@ -12,11 +11,10 @@ class LazyGithub(App): BINDINGS = [("d", "toggle_dark", "Toggle dark mode"), ("q", "quit", "Quit"), ("ctrl+p", "command_palette")] async def authenticate_with_github(self): - config = Config.load_config() try: - access_token = token() - client = GithubClient(config, access_token) - self.push_screen(LazyGithubMainScreen(client)) + # We pull the user here to validate auth + _ = await github.user() + self.push_screen(LazyGithubMainScreen()) except GithubAuthenticationRequired: log("Triggering auth with github") self.push_screen(AuthenticationModal(id="auth-modal")) diff --git a/lazy_github/ui/screens/auth.py b/lazy_github/ui/screens/auth.py index 86decd9..f909899 100644 --- a/lazy_github/ui/screens/auth.py +++ b/lazy_github/ui/screens/auth.py @@ -9,8 +9,6 @@ from textual.widgets import Footer import lazy_github.lib.github.auth as auth -from lazy_github.lib.config import Config -from lazy_github.lib.github.client import GithubClient from lazy_github.ui.screens.primary import LazyGithubMainScreen @@ -71,7 +69,7 @@ async def check_access_token(self, device_code: auth.DeviceCodeResponse): log("Successfully authenticated!") auth.save_access_token(access_token) self.access_token_timer.stop() - self.app.switch_screen(LazyGithubMainScreen(GithubClient(Config.load_config(), auth.token()))) + self.app.switch_screen(LazyGithubMainScreen()) @work async def get_device_token(self): diff --git a/lazy_github/ui/screens/edit_issue.py b/lazy_github/ui/screens/edit_issue.py index c2a1c01..a4eb240 100644 --- a/lazy_github/ui/screens/edit_issue.py +++ b/lazy_github/ui/screens/edit_issue.py @@ -5,7 +5,6 @@ from textual.widgets import Button, Input, Label, Rule, TextArea from lazy_github.lib.github import issues -from lazy_github.lib.github.client import GithubClient from lazy_github.models.github import Issue @@ -20,9 +19,8 @@ class EditIssueContainer(Container): } """ - def __init__(self, client: GithubClient, issue: Issue, *args, **kwargs) -> None: + def __init__(self, issue: Issue, *args, **kwargs) -> None: super().__init__(*args, **kwargs) - self.client = client self.issue = issue def compose(self) -> ComposeResult: @@ -49,7 +47,7 @@ async def submit_updated_issue(self, save_button: Button) -> None: updated_title = self.query_one("#updated_issue_title", Input).value updated_body = self.query_one("#updated_issue_body", TextArea).text self.notify(f"Updating issue #{self.issue.number}...", timeout=1) - await issues.update_issue(self.client, self.issue, title=updated_title, body=updated_body) + await issues.update_issue(self.issue, title=updated_title, body=updated_body) self.notify(f"Successfully updated issue #{self.issue.number}") self.app.pop_screen() @@ -69,10 +67,9 @@ class EditIssueModal(ModalScreen): } """ - def __init__(self, client: GithubClient, issue: Issue, *args, **kwargs): + def __init__(self, issue: Issue, *args, **kwargs): super().__init__(*args, **kwargs) self.issue = issue - self.client = client def compose(self) -> ComposeResult: - yield EditIssueContainer(self.client, self.issue) + yield EditIssueContainer(self.issue) diff --git a/lazy_github/ui/screens/new_comment.py b/lazy_github/ui/screens/new_comment.py index d13408c..720adf5 100644 --- a/lazy_github/ui/screens/new_comment.py +++ b/lazy_github/ui/screens/new_comment.py @@ -6,7 +6,6 @@ from textual.widgets import Button, Label, Markdown, Rule, TextArea from lazy_github.lib.github import issues, pull_requests -from lazy_github.lib.github.client import GithubClient from lazy_github.models.github import Issue, IssueComment, Repository, Review, ReviewComment from lazy_github.ui.widgets.command_log import log_event @@ -53,7 +52,6 @@ class NewCommentContainer(Container): def __init__( self, - client: GithubClient, repo: Repository, issue: Issue, reply_to: CommmentReplyTarget | None, @@ -62,7 +60,6 @@ def __init__( ) -> None: super().__init__(*args, **kwargs) self.reply_to = reply_to - self.client = client self.repo = repo self.issue = issue @@ -86,9 +83,9 @@ async def post_comment(self, _: Button.Pressed) -> None: body = self.query_one("#new_comment_body", TextArea).text try: if isinstance(self.reply_to, ReviewComment): - await pull_requests.reply_to_review_comment(self.client, self.repo, self.issue, self.reply_to, body) + await pull_requests.reply_to_review_comment(self.repo, self.issue, self.reply_to, body) else: - await issues.create_comment(self.client, self.issue, body) + await issues.create_comment(self.issue, body) except HTTPStatusError as hse: # TODO: We should handle the error case better here log_event(f"Error while posting comment for issue #{self.issue.number}: {hse}") @@ -123,7 +120,6 @@ class NewCommentModal(ModalScreen): def __init__( self, - client: GithubClient, repo: Repository, issue: Issue, reply_to: CommmentReplyTarget | None, @@ -132,12 +128,11 @@ def __init__( ) -> None: super().__init__(*args, **kwargs) self.reply_to = reply_to - self.client = client self.repo = repo self.issue = issue def compose(self) -> ComposeResult: - yield NewCommentContainer(self.client, self.repo, self.issue, self.reply_to) + yield NewCommentContainer(self.repo, self.issue, self.reply_to) def action_cancel(self) -> None: self.app.pop_screen() diff --git a/lazy_github/ui/screens/primary.py b/lazy_github/ui/screens/primary.py index 43d135e..af44712 100644 --- a/lazy_github/ui/screens/primary.py +++ b/lazy_github/ui/screens/primary.py @@ -11,7 +11,7 @@ from textual.widget import Widget from textual.widgets import Footer, TabbedContent -from lazy_github.lib.github.client import GithubClient +from lazy_github.lib.config import Config from lazy_github.lib.github.issues import list_issues from lazy_github.lib.github.pull_requests import get_full_pull_request from lazy_github.lib.messages import IssuesAndPullRequestsFetched, IssueSelected, PullRequestSelected, RepoSelected @@ -98,22 +98,19 @@ class SelectionsPane(Container): } """ - def __init__(self, client: GithubClient, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - self.client = client - def compose(self) -> ComposeResult: - yield ReposContainer(self.client, id="repos") - pulls = PullRequestsContainer(self.client, id="pull_requests") - pulls.display = self.client.config.appearance.show_pull_requests + config = Config.load_config() + yield ReposContainer(id="repos") + pulls = PullRequestsContainer(id="pull_requests") + pulls.display = config.appearance.show_pull_requests yield pulls - issues = IssuesContainer(self.client, id="issues") - issues.display = self.client.config.appearance.show_issues + issues = IssuesContainer(id="issues") + issues.display = config.appearance.show_issues yield issues actions = ActionsContainer(id="actions") - actions.display = self.client.config.appearance.show_actions + actions.display = config.appearance.show_actions yield actions @property @@ -131,11 +128,12 @@ def actions(self) -> ActionsContainer: async def on_repo_selected(self, message: RepoSelected) -> None: # self.actions.post_message(message) try: - state_filter = self.client.config.issues.state_filter - owner_filter = self.client.config.issues.owner_filter + config = Config.load_config() + state_filter = config.issues.state_filter + owner_filter = config.issues.owner_filter issues_and_pull_requests = [] if self.pull_requests.display or self.issues.display: - issues_and_pull_requests = await list_issues(self.client, message.repo, state_filter, owner_filter) + issues_and_pull_requests = await list_issues(message.repo, state_filter, owner_filter) except HTTPStatusError as hse: if hse.response.status_code == 404: pass @@ -148,14 +146,11 @@ async def on_repo_selected(self, message: RepoSelected) -> None: class SelectionDetailsPane(Container): - def __init__(self, client: GithubClient, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - self.client = client - def compose(self) -> ComposeResult: + config = Config.load_config() yield SelectionDetailsContainer(id="selection_details") command_log_section = CommandLogSection(id="command_log") - command_log_section.display = self.client.config.appearance.show_command_log + command_log_section.display = config.appearance.show_command_log yield command_log_section @@ -169,10 +164,6 @@ class MainViewPane(Container): ("6", "focus_section('LazyGithubCommandLog')"), ] - def __init__(self, client: GithubClient, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - self.client = client - def action_focus_section(self, selector: str) -> None: self.query_one(selector).focus() @@ -182,28 +173,28 @@ def action_focus_tabs(self) -> None: tabs.children[0].focus() def compose(self) -> ComposeResult: - yield SelectionsPane(self.client) - yield SelectionDetailsPane(self.client, id="details_pane") + yield SelectionsPane() + yield SelectionDetailsPane(id="details_pane") @property def details(self) -> SelectionDetailsContainer: return self.query_one("#selection_details", SelectionDetailsContainer) async def on_pull_request_selected(self, message: PullRequestSelected) -> None: - full_pr = await get_full_pull_request(self.client, message.pr) + full_pr = await get_full_pull_request(message.pr) tabbed_content = self.query_one("#selection_detail_tabs", TabbedContent) await tabbed_content.clear_panes() await tabbed_content.add_pane(PrOverviewTabPane(full_pr)) - await tabbed_content.add_pane(PrDiffTabPane(self.client, full_pr)) - await tabbed_content.add_pane(PrConversationTabPane(self.client, full_pr)) + await tabbed_content.add_pane(PrDiffTabPane(full_pr)) + await tabbed_content.add_pane(PrConversationTabPane(full_pr)) tabbed_content.children[0].focus() self.details.border_title = f"[5] PR #{full_pr.number} Details" async def on_issue_selected(self, message: IssueSelected) -> None: tabbed_content = self.query_one("#selection_detail_tabs", TabbedContent) await tabbed_content.clear_panes() - await tabbed_content.add_pane(IssueOverviewTabPane(self.client, message.issue)) - await tabbed_content.add_pane(IssueConversationTabPane(self.client, message.issue)) + await tabbed_content.add_pane(IssueOverviewTabPane(message.issue)) + await tabbed_content.add_pane(IssueConversationTabPane(message.issue)) tabbed_content.children[0].focus() self.details.border_title = f"[5] Issue #{message.issue.number} Details" @@ -253,14 +244,10 @@ class LazyGithubMainScreen(Screen): COMMANDS = {MainScreenCommandProvider} - def __init__(self, client: GithubClient, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - self.client = client - def compose(self): with Container(): yield LazyGithubStatusSummary() - yield MainViewPane(self.client) + yield MainViewPane() yield Footer() async def action_toggle_ui(self, ui_to_hide: str): @@ -268,7 +255,7 @@ async def action_toggle_ui(self, ui_to_hide: str): widget.display = not widget.display async def action_show_settings_modal(self) -> None: - self.app.push_screen(SettingsModal(self.client.config)) + self.app.push_screen(SettingsModal()) def on_repo_selected(self, message: RepoSelected) -> None: self.query_one("#currently_selected_repo", CurrentlySelectedRepo).current_repo_name = message.repo.full_name diff --git a/lazy_github/ui/screens/settings.py b/lazy_github/ui/screens/settings.py index c378270..202207c 100644 --- a/lazy_github/ui/screens/settings.py +++ b/lazy_github/ui/screens/settings.py @@ -99,9 +99,9 @@ class SettingsContainer(Container): BINDINGS = [("q", "exit_settings", "Exit settings")] - def __init__(self, config: Config) -> None: + def __init__(self) -> None: super().__init__() - self.config = config + self.config = Config.load_config() def compose(self) -> ComposeResult: yield Markdown("# LazyGithub Settings") @@ -166,9 +166,5 @@ class SettingsModal(ModalScreen): } """ - def __init__(self, config: Config) -> None: - super().__init__() - self.config = config - def compose(self) -> ComposeResult: - yield SettingsContainer(self.config) + yield SettingsContainer() diff --git a/lazy_github/ui/widgets/conversations.py b/lazy_github/ui/widgets/conversations.py index 31d3665..cca4a19 100644 --- a/lazy_github/ui/widgets/conversations.py +++ b/lazy_github/ui/widgets/conversations.py @@ -2,7 +2,6 @@ from textual.containers import Container from textual.widgets import Collapsible, Label, Markdown -from lazy_github.lib.github.client import GithubClient from lazy_github.lib.github.pull_requests import ReviewCommentNode from lazy_github.models.github import FullPullRequest, Issue, IssueComment, Review, ReviewComment, ReviewState from lazy_github.ui.screens.new_comment import NewCommentModal @@ -37,9 +36,8 @@ class IssueCommentContainer(Container, can_focus=True): BINDINGS = [("r", "reply_to_individual_comment", "Reply to comment")] - def __init__(self, client: GithubClient, issue: Issue, comment: IssueComment) -> None: + def __init__(self, issue: Issue, comment: IssueComment) -> None: super().__init__() - self.client = client self.issue = issue self.comment = comment @@ -50,7 +48,7 @@ def compose(self) -> ComposeResult: yield Label(f"{author} • {comment_time}", classes="comment-author") def action_reply_to_individual_comment(self) -> None: - self.app.push_screen(NewCommentModal(self.client, self.issue.repo, self.issue, self.comment)) + self.app.push_screen(NewCommentModal(self.issue.repo, self.issue, self.comment)) class ReviewConversation(Container): @@ -62,9 +60,8 @@ class ReviewConversation(Container): } """ - def __init__(self, client: GithubClient, pr: FullPullRequest, root_conversation_node: ReviewCommentNode) -> None: + def __init__(self, pr: FullPullRequest, root_conversation_node: ReviewCommentNode) -> None: super().__init__() - self.client = client self.pr = pr self.root_conversation_node = root_conversation_node @@ -76,7 +73,7 @@ def _flatten_comments(self, root: ReviewCommentNode) -> list[ReviewComment]: def compose(self) -> ComposeResult: for comment in self._flatten_comments(self.root_conversation_node): - yield IssueCommentContainer(self.client, self.pr, comment) + yield IssueCommentContainer(self.pr, comment) class ReviewContainer(Collapsible, can_focus=True): @@ -91,11 +88,8 @@ class ReviewContainer(Collapsible, can_focus=True): """ BINDINGS = [("r", "reply_to_review", "Reply to review")] - def __init__( - self, client: GithubClient, pr: FullPullRequest, review: Review, hierarchy: dict[int, ReviewCommentNode] - ) -> None: + def __init__(self, pr: FullPullRequest, review: Review, hierarchy: dict[int, ReviewCommentNode]) -> None: super().__init__() - self.client = client self.pr = pr self.review = review self.hierarchy = hierarchy @@ -111,7 +105,7 @@ def compose(self) -> ComposeResult: yield Markdown(self.review.body) for comment in self.review.comments: if comment_node := self.hierarchy[comment.id]: - yield ReviewConversation(self.client, self.pr, comment_node) + yield ReviewConversation(self.pr, comment_node) def action_reply_to_review(self) -> None: - self.app.push_screen(NewCommentModal(self.client, self.pr.repo, self.pr, self.review)) + self.app.push_screen(NewCommentModal(self.pr.repo, self.pr, self.review)) diff --git a/lazy_github/ui/widgets/issues.py b/lazy_github/ui/widgets/issues.py index b1aa622..c75abd8 100644 --- a/lazy_github/ui/widgets/issues.py +++ b/lazy_github/ui/widgets/issues.py @@ -6,7 +6,6 @@ from textual.coordinate import Coordinate from textual.widgets import Label, Markdown, Rule, TabPane -from lazy_github.lib.github.client import GithubClient from lazy_github.lib.github.issues import get_comments from lazy_github.lib.messages import IssuesAndPullRequestsFetched, IssueSelected from lazy_github.lib.string_utils import link @@ -25,10 +24,6 @@ class IssuesContainer(LazyGithubContainer): number_column_index = -1 title_column_index = -1 - def __init__(self, client: GithubClient, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - self.client = client - def compose(self) -> ComposeResult: self.border_title = "[3] Issues" yield SearchableLazyGithubDataTable( @@ -76,7 +71,7 @@ async def get_selected_issue(self) -> Issue: async def action_edit_issue(self) -> None: issue = await self.get_selected_issue() - self.app.push_screen(EditIssueModal(self.client, issue)) + self.app.push_screen(EditIssueModal(issue)) @on(LazyGithubDataTable.RowSelected, "#issues_table") async def issue_selected(self) -> None: @@ -94,9 +89,8 @@ class IssueOverviewTabPane(TabPane): BINDINGS = [("E", "edit_issue", "Edit Issue")] - def __init__(self, client: GithubClient, issue: Issue) -> None: + def __init__(self, issue: Issue) -> None: super().__init__("Overview", id="issue_overview_pane") - self.client = client self.issue = issue def compose(self) -> ComposeResult: @@ -115,13 +109,12 @@ def compose(self) -> ComposeResult: yield Markdown(self.issue.body) def action_edit_issue(self) -> None: - self.app.push_screen(EditIssueModal(self.client, self.issue)) + self.app.push_screen(EditIssueModal(self.issue)) class IssueConversationTabPane(TabPane): - def __init__(self, client: GithubClient, issue: Issue) -> None: + def __init__(self, issue: Issue) -> None: super().__init__("Comments", id="issue_conversation") - self.client = client self.issue = issue @property @@ -137,11 +130,11 @@ def on_mount(self) -> None: @work async def fetch_issue_comments(self) -> None: - comments = await get_comments(self.client, self.issue) + comments = await get_comments(self.issue) self.comments.remove_children() if comments: for comment in comments: - comment_container = IssueCommentContainer(self.client, self.issue, comment) + comment_container = IssueCommentContainer(self.issue, comment) self.comments.mount(comment_container) else: self.comments.mount(Label("No comments available")) diff --git a/lazy_github/ui/widgets/pull_requests.py b/lazy_github/ui/widgets/pull_requests.py index 4d795bb..5c5d609 100644 --- a/lazy_github/ui/widgets/pull_requests.py +++ b/lazy_github/ui/widgets/pull_requests.py @@ -5,7 +5,6 @@ from textual.coordinate import Coordinate from textual.widgets import Label, Markdown, RichLog, Rule, TabPane -from lazy_github.lib.github.client import GithubClient from lazy_github.lib.github.issues import get_comments from lazy_github.lib.github.pull_requests import ( get_diff, @@ -26,13 +25,12 @@ class PullRequestsContainer(LazyGithubContainer): This container includes the primary datatable for viewing pull requests on the UI. """ - def __init__(self, client: GithubClient, *args, **kwargs) -> None: - self.client = client + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) self.pull_requests: dict[int, PartialPullRequest] = {} self.status_column_index = -1 self.number_column_index = -1 self.title_column_index = -1 - super().__init__(*args, **kwargs) def compose(self) -> ComposeResult: self.border_title = "[2] Pull Requests" @@ -137,9 +135,8 @@ def compose(self) -> ComposeResult: class PrDiffTabPane(TabPane): - def __init__(self, client: GithubClient, pr: FullPullRequest) -> None: + def __init__(self, pr: FullPullRequest) -> None: super().__init__("Diff", id="diff_pane") - self.client = client self.pr = pr def compose(self) -> ComposeResult: @@ -150,7 +147,7 @@ def compose(self) -> ComposeResult: async def fetch_diff(self): diff_contents = self.query_one("#diff_contents", RichLog) try: - diff = await get_diff(self.client, self.pr) + diff = await get_diff(self.pr) except HTTPStatusError as hse: if hse.response.status_code == 404: diff_contents.write("No diff contents found") @@ -168,9 +165,8 @@ def on_mount(self) -> None: class PrConversationTabPane(TabPane): BINDINGS = [("n", "new_comment", "New comment")] - def __init__(self, client: GithubClient, pr: FullPullRequest) -> None: + def __init__(self, pr: FullPullRequest) -> None: super().__init__("Conversation", id="conversation_pane") - self.client = client self.pr = pr def compose(self) -> ComposeResult: @@ -182,21 +178,21 @@ def comments_and_reviews(self) -> VerticalScroll: @work async def fetch_conversation(self) -> None: - reviews = await get_reviews(self.client, self.pr) + reviews = await get_reviews(self.pr) review_hierarchy = reconstruct_review_conversation_hierarchy(reviews) - comments = await get_comments(self.client, self.pr) + comments = await get_comments(self.pr) self.comments_and_reviews.remove_children() handled_comment_node_ids: list[int] = [] for review in reviews: if review.body: handled_comment_node_ids.extend([c.id for c in review.comments]) - review_container = ReviewContainer(self.client, self.pr, review, review_hierarchy) + review_container = ReviewContainer(self.pr, review, review_hierarchy) self.comments_and_reviews.mount(review_container) for comment in comments: if comment.body and comment.id not in handled_comment_node_ids: - comment_container = IssueCommentContainer(self.client, self.pr, comment) + comment_container = IssueCommentContainer(self.pr, comment) self.comments_and_reviews.mount(comment_container) if len(self.comments_and_reviews.children) == 0: @@ -209,4 +205,4 @@ def on_mount(self) -> None: self.fetch_conversation() def action_new_comment(self) -> None: - self.app.push_screen(NewCommentModal(self.client, self.pr.repo, self.pr, None)) + self.app.push_screen(NewCommentModal(self.pr.repo, self.pr, None)) diff --git a/lazy_github/ui/widgets/repositories.py b/lazy_github/ui/widgets/repositories.py index 4b18f06..45cac37 100644 --- a/lazy_github/ui/widgets/repositories.py +++ b/lazy_github/ui/widgets/repositories.py @@ -7,7 +7,6 @@ import lazy_github.lib.github.repositories as repos_api from lazy_github.lib.config import Config from lazy_github.lib.constants import IS_FAVORITED, favorite_string, private_string -from lazy_github.lib.github.client import GithubClient from lazy_github.lib.messages import RepoSelected from lazy_github.models.github import Repository from lazy_github.ui.widgets.command_log import log_event @@ -20,9 +19,8 @@ class ReposContainer(LazyGithubContainer): ("enter", "select"), ] - def __init__(self, client: GithubClient, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) - self.client = client self.repos: Dict[str, Repository] = {} self.favorite_column_index = -1 self.owner_column_index = 1 @@ -84,7 +82,7 @@ async def add_repos_to_table(self, repos: Iterable[Repository]) -> None: @work async def load_repos(self) -> None: - repos = await repos_api.list_all(self.client) + repos = await repos_api.list_all() self.add_repos_to_table(repos) async def action_toggle_favorite_repo(self):