diff --git a/internal/git/prs.go b/internal/git/prs.go index 10d8066..ac3017e 100644 --- a/internal/git/prs.go +++ b/internal/git/prs.go @@ -14,6 +14,33 @@ func FetchAndMatchPRs(repos []RepoInfo) { matchPRs(repos, reposWithPRs) } +// FetchAndMatchPRForRepo fetches open PRs authored by the current user for a +// single repo and updates the repo's PR fields to match its currently +// checked-out branch. It queries the repo directly with `gh pr list` rather than +// the broad cross-repo search, so it stays fast for a single-repo refresh. +func FetchAndMatchPRForRepo(repo *RepoInfo) { + prs := fetchPRDetails([]RepoInfo{*repo}) + + repo.PRNumber = 0 + repo.PRUrl = "" + repo.PRState = "" + + var best *prDetail + for i := range prs { + if prs[i].HeadRefName != repo.Branch { + continue + } + if best == nil || prs[i].Number < best.Number { + best = &prs[i] + } + } + if best != nil { + repo.PRNumber = best.Number + repo.PRUrl = best.URL + repo.PRState = best.State + } +} + // fetchOpenPRs does a broad search to find which repos have open PRs authored by // the current user. It returns only repo keys (owner/repo), not full PR details, // because gh search prs does not include headRefName — the branch name needed to diff --git a/internal/ui/tui/model.go b/internal/ui/tui/model.go index 614f26d..c38008a 100644 --- a/internal/ui/tui/model.go +++ b/internal/ui/tui/model.go @@ -39,6 +39,7 @@ type scanStartedMsg struct { type repoScannedMsg git.ScanResult type scanDoneMsg struct{} type prsLoadedMsg []git.RepoInfo +type singleRepoPRLoadedMsg git.RepoInfo type commitsLoadedMsg []string type behindCommitsLoadedMsg []string type fetchDoneMsg git.RepoInfo @@ -385,6 +386,13 @@ func loadPRsCmd(repos []git.RepoInfo) tea.Cmd { } } +func loadPRForRepoCmd(repo git.RepoInfo) tea.Cmd { + return func() tea.Msg { + git.FetchAndMatchPRForRepo(&repo) + return singleRepoPRLoadedMsg(repo) + } +} + func loadCommitsCmd(path string) tea.Cmd { return func() tea.Msg { return commitsLoadedMsg(git.RecentCommits(path, 15)) diff --git a/internal/ui/tui/update.go b/internal/ui/tui/update.go index 6f8480d..939b9f7 100644 --- a/internal/ui/tui/update.go +++ b/internal/ui/tui/update.go @@ -95,6 +95,34 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } return m, nil + case singleRepoPRLoadedMsg: + updated := git.RepoInfo(msg) + activePath := "" + if m.state == stateDetail && m.cursor < len(m.repos) { + activePath = m.repos[m.cursor].Path + } + for i, r := range m.repos { + if r.Path == updated.Path { + m.repos[i] = updated + break + } + } + m.prsLoading = false + m.prsEverLoaded = true + git.SortReposByCol(m.repos, m.sortCol, m.sortDesc) + if activePath != "" { + for i, r := range m.repos { + if r.Path == activePath { + m.cursor = i + break + } + } + } + if m.state == stateDetail { + m.detailVP.SetContent(m.renderDetailContent()) + } + return m, nil + case commitsLoadedMsg: m.detailCommits = []string(msg) m.commitsLoaded = true @@ -150,6 +178,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } else { m.behindLoaded = true } + if !m.noPRs { + m.prsLoading = true + cmds = append(cmds, m.spinner.Tick, loadPRForRepoCmd(updated)) + } return m, tea.Batch(cmds...) } return m, nil