From bcf4fdcbf007c832b55b9f425a6bc2c737033b08 Mon Sep 17 00:00:00 2001 From: ch8matt Date: Wed, 25 Feb 2026 08:07:49 +0000 Subject: [PATCH] feat(github): list repositories starred by authenticated user via /user/starred --- src/github.cpp | 8 ++++++ src/github.h | 3 +++ src/handlers.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ src/handlers.h | 9 +++++++ src/plugin.cpp | 1 + 5 files changed, 91 insertions(+) diff --git a/src/github.cpp b/src/github.cpp index 54ed2ce..d9438c5 100644 --- a/src/github.cpp +++ b/src/github.cpp @@ -111,6 +111,14 @@ QNetworkReply *RestApi::notifications() const {{u"all"_s, u"true"_s}})); } +QNetworkReply *RestApi::starredRepositories(int per_page, int page) const +{ + // https://docs.github.com/en/rest/activity/starring#list-repositories-starred-by-the-authenticated-user + return network().get(request(u"/user/starred"_s, + {{u"per_page"_s, QString::number(per_page)}, + {u"page"_s, QString::number(page)}})); +} + QNetworkReply *RestApi::searchUsers(const QString &query, int per_page, int page) const { // https://docs.github.com/en/rest/search/search#search-users diff --git a/src/github.h b/src/github.h index 646d4f7..14965c0 100644 --- a/src/github.h +++ b/src/github.h @@ -35,6 +35,9 @@ class RestApi int per_page, int page) const; + /// Requires ``repo`` scope + [[nodiscard]] QNetworkReply* starredRepositories(int per_page, int page) const; + /// Requires no scopes (if public data is sufficient) [[nodiscard]] QNetworkReply* searchIssues(const QString &query, int per_page, diff --git a/src/handlers.cpp b/src/handlers.cpp index ad8d6eb..152dc91 100644 --- a/src/handlers.cpp +++ b/src/handlers.cpp @@ -185,6 +185,76 @@ vector> RepoSearchHandler::defaultSearches() const //-------------------------------------------------------------------------------------------------- +StarredRepoHandler::StarredRepoHandler(const github::RestApi &api): + GithubSearchHandler(u"github.starred"_s, + Plugin::tr("GitHub starred repositories"), + Plugin::tr("Repositories you starred"), + u"ghs"_s, + api) +{} + +QNetworkReply *StarredRepoHandler::requestSearch(const QString &, uint page) const +{ + return api_.starredRepositories(10, static_cast(page)); +} + +shared_ptr StarredRepoHandler::parseItem(const QJsonObject &o) const +{ + return RepositoryItem::fromJson(o); +} + +vector> StarredRepoHandler::defaultSearches() const +{ + return { + { Plugin::tr("My starred repositories"), QString{} } + }; +} + +AsyncItemGenerator StarredRepoHandler::items(QueryContext &ctx) +{ + try { + for (auto page = 1;; ++page) + { + co_await qCoro(rate_limiter_.acquire().get(), &Acquire::granted); + + if (!ctx.isValid()) + co_return; + + unique_ptr reply(requestSearch({}, page)); + DEBG << "Fetch" << reply->request().url(); + co_await qCoro(reply.get()).waitForFinished(); + + const auto var = RestApi::parseJson(*reply); + + if (holds_alternative(var)) + { + const auto arr = get(var).array(); + + auto v = arr | views::transform([this](const auto &val) + { return parseItem(val.toObject()); }); + + vector> items(begin(v), end(v)); + + if (items.empty()) + co_return; // end pagination + + co_yield ::move(items); + } + else + { + vector> items; + items.push_back(makeErrorItem(get(var))); + co_yield ::move(items); + co_return; + } + } + } catch (...) { + CRIT << "EXCEP"; + } +} + +//-------------------------------------------------------------------------------------------------- + IssueSearchHandler::IssueSearchHandler(const github::RestApi &api): GithubSearchHandler(u"github.issues"_s, Plugin::tr("GitHub issues"), diff --git a/src/handlers.h b/src/handlers.h index a102cd6..b754000 100644 --- a/src/handlers.h +++ b/src/handlers.h @@ -80,6 +80,15 @@ class RepoSearchHandler : public GithubSearchHandler std::vector> defaultSearches() const override; }; +class StarredRepoHandler : public GithubSearchHandler +{ +public: + StarredRepoHandler(const github::RestApi&); + albert::AsyncItemGenerator items(albert::QueryContext &) override; // override because /user/starred returns array + QNetworkReply *requestSearch(const QString &query, uint page) const override; + std::shared_ptr parseItem(const QJsonObject &) const override; + std::vector> defaultSearches() const override; +}; class IssueSearchHandler : public GithubSearchHandler { diff --git a/src/plugin.cpp b/src/plugin.cpp index e335719..42aa454 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -41,6 +41,7 @@ Plugin::Plugin() search_handlers_.emplace_back(make_unique(api)); search_handlers_.emplace_back(make_unique(api)); search_handlers_.emplace_back(make_unique(api)); + search_handlers_.emplace_back(make_unique(api)); } Plugin::~Plugin() = default;