@@ -132,7 +134,16 @@
{{ end }}
{{ define "mergeStatus" }}
- {{ if .Pull.State.IsMerged }}
+ {{ if .Pull.State.IsClosed }}
+
@@ -170,70 +189,6 @@
{{ end }}
{{ end }}
-{{ define "actions" }}
- {{ $rootObj := index . 0 }}
- {{ $submissionId := index . 1 }}
-
- {{ with $rootObj }}
- {{ $isPushAllowed := .RepoInfo.Roles.IsPushAllowed }}
- {{ $isMerged := .Pull.State.IsMerged }}
- {{ $isClosed := .Pull.State.IsClosed }}
- {{ $isOpen := .Pull.State.IsOpen }}
- {{ $isConflicted := and .MergeCheck .MergeCheck.IsConflicted }}
- {{ $isPullAuthor := and .LoggedInUser (eq .LoggedInUser.Did .Pull.OwnerDid) }}
-
-
-
-
- {{ i "message-square-plus" "w-4 h-4" }}
- comment
-
- {{ if and $isPushAllowed $isOpen }}
- {{ $disabled := "" }}
- {{ if $isConflicted }}
- {{ $disabled = "disabled" }}
- {{ end }}
-
- {{ i "git-merge" "w-4 h-4" }}
- merge
-
- {{ end }}
-
- {{ if and $isPullAuthor $isOpen }}
-
- {{ i "rotate-ccw" "w-4 h-4" }}
- resubmit
-
- {{ end }}
-
- {{ if and $isPullAuthor $isPushAllowed $isOpen }}
-
- {{ i "ban" "w-4 h-4" }}
- close
-
- {{ end }}
-
- {{ if and $isPullAuthor $isPushAllowed $isClosed }}
-
- {{ i "circle-dot" "w-4 h-4" }}
- reopen
-
- {{ end }}
-
-
- {{ end }}
-{{ end }}
-
{{ define "newComment" }}
{{ $rootObj := index . 0 }}
{{ $submissionId := index . 1 }}
diff --git a/appview/state/pull.go b/appview/state/pull.go
index 218801f..f0eb0ee 100644
--- a/appview/state/pull.go
+++ b/appview/state/pull.go
@@ -20,6 +20,48 @@ import (
lexutil "github.com/bluesky-social/indigo/lex/util"
)
+// htmx fragment
+func (s *State) PullActions(w http.ResponseWriter, r *http.Request) {
+ switch r.Method {
+ case http.MethodGet:
+ user := s.auth.GetUser(r)
+ f, err := fullyResolvedRepo(r)
+ if err != nil {
+ log.Println("failed to get repo and knot", err)
+ return
+ }
+
+ pull, ok := r.Context().Value("pull").(*db.Pull)
+ if !ok {
+ log.Println("failed to get pull")
+ s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
+ return
+ }
+
+ roundNumberStr := chi.URLParam(r, "round")
+ roundNumber, err := strconv.Atoi(roundNumberStr)
+ if err != nil {
+ roundNumber = pull.LastRoundNumber()
+ }
+ if roundNumber >= len(pull.Submissions) {
+ http.Error(w, "bad round id", http.StatusBadRequest)
+ log.Println("failed to parse round id", err)
+ return
+ }
+
+ mergeCheckResponse := s.mergeCheck(f, pull)
+
+ s.pages.PullActionsFragment(w, pages.PullActionsParams{
+ LoggedInUser: user,
+ RepoInfo: f.RepoInfo(s, user),
+ Pull: pull,
+ RoundNumber: roundNumber,
+ MergeCheck: mergeCheckResponse,
+ })
+ return
+ }
+}
+
func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
f, err := fullyResolvedRepo(r)
@@ -62,37 +104,7 @@ func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) {
}
}
- var mergeCheckResponse types.MergeCheckResponse
-
- // Only perform merge check if the pull request is not already merged
- if pull.State != db.PullMerged {
- secret, err := db.GetRegistrationKey(s.db, f.Knot)
- if err != nil {
- log.Printf("failed to get registration key for %s", f.Knot)
- s.pages.Notice(w, "pull", "Failed to load pull request. Try again later.")
- return
- }
-
- ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
- if err == nil {
- resp, err := ksClient.MergeCheck([]byte(pull.LatestPatch()), pull.OwnerDid, f.RepoName, pull.TargetBranch)
- if err != nil {
- log.Println("failed to check for mergeability:", err)
- } else {
- respBody, err := io.ReadAll(resp.Body)
- if err != nil {
- log.Println("failed to read merge check response body")
- } else {
- err = json.Unmarshal(respBody, &mergeCheckResponse)
- if err != nil {
- log.Println("failed to unmarshal merge check response", err)
- }
- }
- }
- } else {
- log.Printf("failed to setup signed client for %s; ignoring...", f.Knot)
- }
- }
+ mergeCheckResponse := s.mergeCheck(f, pull)
s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{
LoggedInUser: user,
@@ -103,6 +115,63 @@ func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) {
})
}
+func (s *State) mergeCheck(f *FullyResolvedRepo, pull *db.Pull) types.MergeCheckResponse {
+ if pull.State == db.PullMerged {
+ return types.MergeCheckResponse{}
+ }
+
+ secret, err := db.GetRegistrationKey(s.db, f.Knot)
+ if err != nil {
+ log.Printf("failed to get registration key: %w", err)
+ return types.MergeCheckResponse{
+ Error: "failed to check merge status: this knot is unregistered",
+ }
+ }
+
+ ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
+ if err != nil {
+ log.Printf("failed to setup signed client for %s; ignoring: %v", f.Knot, err)
+ return types.MergeCheckResponse{
+ Error: "failed to check merge status",
+ }
+ }
+
+ resp, err := ksClient.MergeCheck([]byte(pull.LatestPatch()), pull.OwnerDid, f.RepoName, pull.TargetBranch)
+ if err != nil {
+ log.Println("failed to check for mergeability:", err)
+ switch resp.StatusCode {
+ case 400:
+ return types.MergeCheckResponse{
+ Error: "failed to check merge status: does this knot support PRs?",
+ }
+ default:
+ return types.MergeCheckResponse{
+ Error: "failed to check merge status: this knot is unreachable",
+ }
+ }
+ }
+
+ respBody, err := io.ReadAll(resp.Body)
+ if err != nil {
+ log.Println("failed to read merge check response body")
+ return types.MergeCheckResponse{
+ Error: "failed to check merge status: knot is not speaking the right language",
+ }
+ }
+ defer resp.Body.Close()
+
+ var mergeCheckResponse types.MergeCheckResponse
+ err = json.Unmarshal(respBody, &mergeCheckResponse)
+ if err != nil {
+ log.Println("failed to unmarshal merge check response", err)
+ return types.MergeCheckResponse{
+ Error: "failed to check merge status: knot is not speaking the right language",
+ }
+ }
+
+ return mergeCheckResponse
+}
+
func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
f, err := fullyResolvedRepo(r)
@@ -213,7 +282,23 @@ func (s *State) PullComment(w http.ResponseWriter, r *http.Request) {
return
}
+ roundNumberStr := chi.URLParam(r, "round")
+ roundNumber, err := strconv.Atoi(roundNumberStr)
+ if err != nil || roundNumber >= len(pull.Submissions) {
+ http.Error(w, "bad round id", http.StatusBadRequest)
+ log.Println("failed to parse round id", err)
+ return
+ }
+
switch r.Method {
+ case http.MethodGet:
+ s.pages.PullNewCommentFragment(w, pages.PullNewCommentParams{
+ LoggedInUser: user,
+ RepoInfo: f.RepoInfo(s, user),
+ Pull: pull,
+ RoundNumber: roundNumber,
+ })
+ return
case http.MethodPost:
body := r.FormValue("body")
if body == "" {
@@ -221,13 +306,6 @@ func (s *State) PullComment(w http.ResponseWriter, r *http.Request) {
return
}
- submissionIdstr := r.FormValue("submissionId")
- submissionId, err := strconv.Atoi(submissionIdstr)
- if err != nil {
- s.pages.Notice(w, "pull", "Invalid comment submission.")
- return
- }
-
// Start a transaction
tx, err := s.db.BeginTx(r.Context(), nil)
if err != nil {
@@ -263,6 +341,7 @@ func (s *State) PullComment(w http.ResponseWriter, r *http.Request) {
},
},
})
+ log.Println(atResp.Uri)
if err != nil {
log.Println("failed to create pull comment", err)
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
@@ -276,7 +355,7 @@ func (s *State) PullComment(w http.ResponseWriter, r *http.Request) {
PullId: pull.PullId,
Body: body,
CommentAt: atResp.Uri,
- SubmissionId: submissionId,
+ SubmissionId: pull.Submissions[roundNumber].ID,
})
if err != nil {
log.Println("failed to create pull comment", err)
@@ -433,6 +512,12 @@ func (s *State) ResubmitPull(w http.ResponseWriter, r *http.Request) {
}
switch r.Method {
+ case http.MethodGet:
+ s.pages.PullResubmitFragment(w, pages.PullResubmitParams{
+ RepoInfo: f.RepoInfo(s, user),
+ Pull: pull,
+ })
+ return
case http.MethodPost:
patch := r.FormValue("patch")
diff --git a/appview/state/router.go b/appview/state/router.go
index d919e59..33f586a 100644
--- a/appview/state/router.go
+++ b/appview/state/router.go
@@ -66,13 +66,27 @@ func (s *State) UserRouter() http.Handler {
r.Route("/{pull}", func(r chi.Router) {
r.Use(ResolvePull(s))
r.Get("/", s.RepoSinglePull)
- r.Get("/round/{round}", s.RepoPullPatch)
+
+ r.Route("/round/{round}", func(r chi.Router) {
+ r.Get("/", s.RepoPullPatch)
+ r.Get("/actions", s.PullActions)
+ r.Route("/comment", func(r chi.Router) {
+ r.Get("/", s.PullComment)
+ r.Post("/", s.PullComment)
+ })
+ })
// authorized requests below this point
r.Group(func(r chi.Router) {
r.Use(AuthMiddleware(s))
- r.Post("/resubmit", s.ResubmitPull)
- r.Post("/comment", s.PullComment)
+ r.Route("/resubmit", func(r chi.Router) {
+ r.Get("/", s.ResubmitPull)
+ r.Post("/", s.ResubmitPull)
+ })
+ r.Route("/comment", func(r chi.Router) {
+ r.Get("/", s.PullComment)
+ r.Post("/", s.PullComment)
+ })
r.Post("/close", s.ClosePull)
r.Post("/reopen", s.ReopenPull)
// collaborators only