forked from tangled.org/core
Monorepo for Tangled

add comment and resubmit actions in fragments

improve merge check a bit too

+5 -1
appview/db/pulls.go
··· 101 101 } 102 102 103 103 func (p *Pull) LatestPatch() string { 104 - latestSubmission := p.Submissions[len(p.Submissions)-1] 104 + latestSubmission := p.Submissions[p.LastRoundNumber()] 105 105 return latestSubmission.Patch 106 + } 107 + 108 + func (p *Pull) LastRoundNumber() int { 109 + return len(p.Submissions) - 1 106 110 } 107 111 108 112 func (s PullSubmission) AsNiceDiff(targetBranch string) types.NiceDiff {
+15
appview/pages/funcmap.go
··· 1 1 package pages 2 2 3 3 import ( 4 + "errors" 4 5 "fmt" 5 6 "html" 6 7 "html/template" ··· 120 121 }, 121 122 "list": func(args ...any) []any { 122 123 return args 124 + }, 125 + "dict": func(values ...any) (map[string]any, error) { 126 + if len(values)%2 != 0 { 127 + return nil, errors.New("invalid dict call") 128 + } 129 + dict := make(map[string]any, len(values)/2) 130 + for i := 0; i < len(values); i += 2 { 131 + key, ok := values[i].(string) 132 + if !ok { 133 + return nil, errors.New("dict keys must be strings") 134 + } 135 + dict[key] = values[i+1] 136 + } 137 + return dict, nil 123 138 }, 124 139 "i": func(name string, classes ...string) template.HTML { 125 140 data, err := icon(name, classes)
+34
appview/pages/pages.go
··· 576 576 return p.execute("repo/pulls/patch", w, params) 577 577 } 578 578 579 + type PullResubmitParams struct { 580 + LoggedInUser *auth.User 581 + RepoInfo RepoInfo 582 + Pull *db.Pull 583 + SubmissionId int 584 + } 585 + 586 + func (p *Pages) PullResubmitFragment(w io.Writer, params PullResubmitParams) error { 587 + return p.executePlain("fragments/pullResubmit", w, params) 588 + } 589 + 590 + type PullActionsParams struct { 591 + LoggedInUser *auth.User 592 + RepoInfo RepoInfo 593 + Pull *db.Pull 594 + RoundNumber int 595 + MergeCheck types.MergeCheckResponse 596 + } 597 + 598 + func (p *Pages) PullActionsFragment(w io.Writer, params PullActionsParams) error { 599 + return p.executePlain("fragments/pullActions", w, params) 600 + } 601 + 602 + type PullNewCommentParams struct { 603 + LoggedInUser *auth.User 604 + RepoInfo RepoInfo 605 + Pull *db.Pull 606 + RoundNumber int 607 + } 608 + 609 + func (p *Pages) PullNewCommentFragment(w io.Writer, params PullNewCommentParams) error { 610 + return p.executePlain("fragments/pullNewComment", w, params) 611 + } 612 + 579 613 func (p *Pages) Static() http.Handler { 580 614 sub, err := fs.Sub(Files, "static") 581 615 if err != nil {
+2 -2
appview/pages/templates/fragments/editRepoDescription.html
··· 2 2 <form hx-put="/{{ .RepoInfo.FullName }}/description" hx-target="this" hx-swap="outerHTML" class="flex flex-wrap gap-2"> 3 3 <input type="text" class="p-1" name="description" value="{{ .RepoInfo.Description }}"> 4 4 <button type="submit" class="btn p-2 flex items-center gap-2 no-underline text-sm"> 5 - save {{ i "check" "w-3 h-3" }} 5 + {{ i "check" "w-3 h-3" }} save 6 6 </button> 7 7 <button type="button" class="btn p-2 flex items-center gap-2 no-underline text-sm" hx-get="/{{ .RepoInfo.FullName }}/description" > 8 - cancel {{ i "x" "w-3 h-3" }} 8 + {{ i "x" "w-3 h-3" }} cancel 9 9 </button> 10 10 </form> 11 11 {{ end }}
+72
appview/pages/templates/fragments/pullActions.html
··· 1 + {{ define "fragments/pullActions" }} 2 + {{ $lastIdx := sub (len .Pull.Submissions) 1 }} 3 + {{ $roundNumber := .RoundNumber }} 4 + 5 + {{ $isPushAllowed := .RepoInfo.Roles.IsPushAllowed }} 6 + {{ $isMerged := .Pull.State.IsMerged }} 7 + {{ $isClosed := .Pull.State.IsClosed }} 8 + {{ $isOpen := .Pull.State.IsOpen }} 9 + {{ $isConflicted := and .MergeCheck (or .MergeCheck.Error .MergeCheck.IsConflicted) }} 10 + {{ $isPullAuthor := and .LoggedInUser (eq .LoggedInUser.Did .Pull.OwnerDid) }} 11 + {{ $isLastRound := eq $roundNumber $lastIdx }} 12 + <div class="relative w-fit"> 13 + <div class="absolute left-8 -top-2 w-px h-2 bg-gray-300"></div> 14 + <div id="actions-{{$roundNumber}}" class="flex flex-wrap gap-2"> 15 + <button 16 + hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ $roundNumber }}/comment" 17 + hx-target="#actions-{{$roundNumber}}" 18 + hx-swap="outerHtml" 19 + class="btn p-2 flex items-center gap-2 no-underline hover:no-underline"> 20 + {{ i "message-square-plus" "w-4 h-4" }} 21 + <span>comment</span> 22 + </button> 23 + {{ if and $isPushAllowed $isOpen $isLastRound }} 24 + {{ $disabled := "" }} 25 + {{ if $isConflicted }} 26 + {{ $disabled = "disabled" }} 27 + {{ end }} 28 + <button 29 + hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/merge" 30 + hx-swap="none" 31 + hx-confirm="Are you sure you want to merge this pull request?" 32 + class="btn p-2 flex items-center gap-2" {{ $disabled }}> 33 + {{ i "git-merge" "w-4 h-4" }} 34 + <span>merge</span> 35 + </button> 36 + {{ end }} 37 + 38 + {{ if and $isPullAuthor $isOpen $isLastRound }} 39 + <button 40 + hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/resubmit" 41 + hx-target="#actions-{{$roundNumber}}" 42 + hx-swap="outerHtml" 43 + class="btn p-2 flex items-center gap-2"> 44 + {{ i "rotate-ccw" "w-4 h-4" }} 45 + <span>resubmit</span> 46 + </button> 47 + {{ end }} 48 + 49 + {{ if and $isPullAuthor $isPushAllowed $isOpen $isLastRound }} 50 + <button 51 + hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/close" 52 + hx-swap="none" 53 + class="btn p-2 flex items-center gap-2"> 54 + {{ i "ban" "w-4 h-4" }} 55 + <span>close</span> 56 + </button> 57 + {{ end }} 58 + 59 + {{ if and $isPullAuthor $isPushAllowed $isClosed $isLastRound }} 60 + <button 61 + hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/reopen" 62 + hx-swap="none" 63 + class="btn p-2 flex items-center gap-2"> 64 + {{ i "circle-dot" "w-4 h-4" }} 65 + <span>reopen</span> 66 + </button> 67 + {{ end }} 68 + </div> 69 + </div> 70 + {{ end }} 71 + 72 +
+32
appview/pages/templates/fragments/pullNewComment.html
··· 1 + {{ define "fragments/pullNewComment" }} 2 + <div 3 + id="pull-comment-card-{{ .RoundNumber }}" 4 + class="bg-white rounded drop-shadow-sm py-4 px-6 relative w-full flex flex-col gap-2"> 5 + <div class="text-sm text-gray-500"> 6 + {{ didOrHandle .LoggedInUser.Did .LoggedInUser.Handle }} 7 + </div> 8 + <form 9 + hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ .RoundNumber }}/comment" 10 + hx-swap="none" 11 + class="w-full flex flex-wrap gap-2"> 12 + <textarea 13 + name="body" 14 + class="w-full p-2 rounded border border-gray-200" 15 + placeholder="Add to the discussion..."></textarea> 16 + <button type="submit" class="btn flex items-center gap-2"> 17 + {{ i "message-square" "w-4 h-4" }} comment 18 + </button> 19 + <button 20 + type="button" 21 + class="btn flex items-center gap-2" 22 + hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ .RoundNumber }}/actions" 23 + hx-swap="outerHTML" 24 + hx-target="#pull-comment-card-{{ .RoundNumber }}"> 25 + {{ i "x" "w-4 h-4" }} 26 + <span>cancel</span> 27 + </button> 28 + <div id="pull-comment"></div> 29 + </form> 30 + </div> 31 + {{ end }} 32 +
+52
appview/pages/templates/fragments/pullResubmit.html
··· 1 + {{ define "fragments/pullResubmit" }} 2 + <div 3 + id="resubmit-pull-card" 4 + class="rounded relative border bg-amber-50 border-amber-200 px-6 py-2"> 5 + 6 + <div class="flex items-center gap-2 text-amber-500"> 7 + {{ i "pencil" "w-4 h-4" }} 8 + <span class="font-medium">resubmit your patch</span> 9 + </div> 10 + 11 + <div class="mt-2 text-sm text-gray-700"> 12 + You can update this patch to address any reviews. 13 + This will begin a new round of reviews, 14 + but you'll still be able to view your previous submissions and feedback. 15 + </div> 16 + 17 + <div class="mt-4 flex flex-col"> 18 + <form 19 + hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/resubmit" 20 + hx-swap="none" 21 + class="w-full flex flex-wrap gap-2"> 22 + <textarea 23 + name="patch" 24 + class="w-full p-2 mb-2 rounded border border-gray-200" 25 + placeholder="Paste your updated patch here." 26 + rows="15" 27 + ></textarea> 28 + <button 29 + type="submit" 30 + class="btn flex items-center gap-2" 31 + {{ if or .Pull.State.IsClosed }} 32 + disabled 33 + {{ end }}> 34 + {{ i "rotate-ccw" "w-4 h-4" }} 35 + <span>resubmit</span> 36 + </button> 37 + <button 38 + type="button" 39 + class="btn flex items-center gap-2" 40 + hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ .Pull.LastRoundNumber }}/actions" 41 + hx-swap="outerHTML" 42 + hx-target="#resubmit-pull-card"> 43 + {{ i "x" "w-4 h-4" }} 44 + <span>cancel</span> 45 + </button> 46 + </form> 47 + 48 + <div id="resubmit-error" class="error"></div> 49 + <div id="resubmit-success" class="success"></div> 50 + </div> 51 + </div> 52 + {{ end }}
+1 -2
appview/pages/templates/fragments/repoDescription.html
··· 8 8 9 9 {{ if .RepoInfo.Roles.IsOwner }} 10 10 <button class="btn p-2 flex items-center gap-2 no-underline text-sm" hx-get="/{{ .RepoInfo.FullName }}/description/edit"> 11 - edit 12 - {{ i "pencil" "w-3 h-3" }} 11 + {{ i "pencil" "w-3 h-3" }} edit 13 12 </button> 14 13 {{ end }} 15 14 </span>
+22 -67
appview/pages/templates/repo/pulls/pull.html
··· 115 115 </div> 116 116 {{ end }} 117 117 118 - {{ block "mergeStatus" $ }} {{ end }} 118 + {{ if eq $lastIdx .RoundNumber }} 119 + {{ block "mergeStatus" $ }} {{ end }} 120 + {{ end }} 119 121 120 122 {{ if $.LoggedInUser }} 121 - {{ block "actions" (list $ .ID) }} {{ end }} 123 + {{ template "fragments/pullActions" (dict "LoggedInUser" $.LoggedInUser "Pull" $.Pull "RepoInfo" $.RepoInfo "RoundNumber" .RoundNumber "MergeCheck" $.MergeCheck) }} 122 124 {{ else }} 123 125 <div class="bg-white rounded drop-shadow-sm px-6 py-4 w-fit"> 124 126 <div class="absolute left-8 -top-2 w-px h-2 bg-gray-300"></div> ··· 132 134 {{ end }} 133 135 134 136 {{ define "mergeStatus" }} 135 - {{ if .Pull.State.IsMerged }} 137 + {{ if .Pull.State.IsClosed }} 138 + <div class="bg-gray-50 border border-black rounded drop-shadow-sm px-6 py-2 relative w-fit"> 139 + <div class="absolute left-8 -top-2 w-px h-2 bg-gray-300"></div> 140 + <div class="flex items-center gap-2 text-black"> 141 + {{ i "ban" "w-4 h-4" }} 142 + <span class="font-medium">closed without merging</span 143 + > 144 + </div> 145 + </div> 146 + {{ else if .Pull.State.IsMerged }} 136 147 <div class="bg-purple-50 border border-purple-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 137 148 <div class="absolute left-8 -top-2 w-px h-2 bg-gray-300"></div> 138 149 <div class="flex items-center gap-2 text-purple-500"> ··· 141 152 > 142 153 </div> 143 154 </div> 155 + {{ else if and .MergeCheck .MergeCheck.Error }} 156 + <div class="bg-red-50 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 157 + <div class="absolute left-8 -top-2 w-px h-2 bg-gray-300"></div> 158 + <div class="flex items-center gap-2 text-red-500"> 159 + {{ i "triangle-alert" "w-4 h-4" }} 160 + <span class="font-medium">{{ .MergeCheck.Error }}</span> 161 + </div> 162 + </div> 144 163 {{ else if and .MergeCheck .MergeCheck.IsConflicted }} 145 164 <div class="bg-red-50 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 146 165 <div class="absolute left-8 -top-2 w-px h-2 bg-gray-300"></div> ··· 167 186 <span class="font-medium">no conflicts, ready to merge</span> 168 187 </div> 169 188 </div> 170 - {{ end }} 171 - {{ end }} 172 - 173 - {{ define "actions" }} 174 - {{ $rootObj := index . 0 }} 175 - {{ $submissionId := index . 1 }} 176 - 177 - {{ with $rootObj }} 178 - {{ $isPushAllowed := .RepoInfo.Roles.IsPushAllowed }} 179 - {{ $isMerged := .Pull.State.IsMerged }} 180 - {{ $isClosed := .Pull.State.IsClosed }} 181 - {{ $isOpen := .Pull.State.IsOpen }} 182 - {{ $isConflicted := and .MergeCheck .MergeCheck.IsConflicted }} 183 - {{ $isPullAuthor := and .LoggedInUser (eq .LoggedInUser.Did .Pull.OwnerDid) }} 184 - <div class="relative w-fit"> 185 - <div class="absolute left-8 -top-2 w-px h-2 bg-gray-300"></div> 186 - <div class="flex flex-wrap gap-2"> 187 - <button href="#" class="btn p-2 flex items-center gap-2 no-underline hover:no-underline"> 188 - {{ i "message-square-plus" "w-4 h-4" }} 189 - <span>comment</span> 190 - </button> 191 - {{ if and $isPushAllowed $isOpen }} 192 - {{ $disabled := "" }} 193 - {{ if $isConflicted }} 194 - {{ $disabled = "disabled" }} 195 - {{ end }} 196 - <button 197 - hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/merge" 198 - hx-swap="none" 199 - hx-confirm="Are you sure you want to merge this pull request?" 200 - class="btn p-2 flex items-center gap-2" {{ $disabled }}> 201 - {{ i "git-merge" "w-4 h-4" }} 202 - <span>merge</span> 203 - </button> 204 - {{ end }} 205 - 206 - {{ if and $isPullAuthor $isOpen }} 207 - <button href="#" class="btn p-2 flex items-center gap-2 no-underline hover:no-underline"> 208 - {{ i "rotate-ccw" "w-4 h-4" }} 209 - <span>resubmit</span> 210 - </button> 211 - {{ end }} 212 - 213 - {{ if and $isPullAuthor $isPushAllowed $isOpen }} 214 - <button 215 - hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/close" 216 - hx-swap="none" 217 - class="btn p-2 flex items-center gap-2"> 218 - {{ i "ban" "w-4 h-4" }} 219 - <span>close</span> 220 - </button> 221 - {{ end }} 222 - 223 - {{ if and $isPullAuthor $isPushAllowed $isClosed }} 224 - <button 225 - hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/reopen" 226 - hx-swap="none" 227 - class="btn p-2 flex items-center gap-2"> 228 - {{ i "circle-dot" "w-4 h-4" }} 229 - <span>reopen</span> 230 - </button> 231 - {{ end }} 232 - </div> 233 - </div> 234 189 {{ end }} 235 190 {{ end }} 236 191
+124 -39
appview/state/pull.go
··· 20 20 lexutil "github.com/bluesky-social/indigo/lex/util" 21 21 ) 22 22 23 + // htmx fragment 24 + func (s *State) PullActions(w http.ResponseWriter, r *http.Request) { 25 + switch r.Method { 26 + case http.MethodGet: 27 + user := s.auth.GetUser(r) 28 + f, err := fullyResolvedRepo(r) 29 + if err != nil { 30 + log.Println("failed to get repo and knot", err) 31 + return 32 + } 33 + 34 + pull, ok := r.Context().Value("pull").(*db.Pull) 35 + if !ok { 36 + log.Println("failed to get pull") 37 + s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 38 + return 39 + } 40 + 41 + roundNumberStr := chi.URLParam(r, "round") 42 + roundNumber, err := strconv.Atoi(roundNumberStr) 43 + if err != nil { 44 + roundNumber = pull.LastRoundNumber() 45 + } 46 + if roundNumber >= len(pull.Submissions) { 47 + http.Error(w, "bad round id", http.StatusBadRequest) 48 + log.Println("failed to parse round id", err) 49 + return 50 + } 51 + 52 + mergeCheckResponse := s.mergeCheck(f, pull) 53 + 54 + s.pages.PullActionsFragment(w, pages.PullActionsParams{ 55 + LoggedInUser: user, 56 + RepoInfo: f.RepoInfo(s, user), 57 + Pull: pull, 58 + RoundNumber: roundNumber, 59 + MergeCheck: mergeCheckResponse, 60 + }) 61 + return 62 + } 63 + } 64 + 23 65 func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) { 24 66 user := s.auth.GetUser(r) 25 67 f, err := fullyResolvedRepo(r) ··· 62 104 } 63 105 } 64 106 65 - var mergeCheckResponse types.MergeCheckResponse 66 - 67 - // Only perform merge check if the pull request is not already merged 68 - if pull.State != db.PullMerged { 69 - secret, err := db.GetRegistrationKey(s.db, f.Knot) 70 - if err != nil { 71 - log.Printf("failed to get registration key for %s", f.Knot) 72 - s.pages.Notice(w, "pull", "Failed to load pull request. Try again later.") 73 - return 74 - } 75 - 76 - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) 77 - if err == nil { 78 - resp, err := ksClient.MergeCheck([]byte(pull.LatestPatch()), pull.OwnerDid, f.RepoName, pull.TargetBranch) 79 - if err != nil { 80 - log.Println("failed to check for mergeability:", err) 81 - } else { 82 - respBody, err := io.ReadAll(resp.Body) 83 - if err != nil { 84 - log.Println("failed to read merge check response body") 85 - } else { 86 - err = json.Unmarshal(respBody, &mergeCheckResponse) 87 - if err != nil { 88 - log.Println("failed to unmarshal merge check response", err) 89 - } 90 - } 91 - } 92 - } else { 93 - log.Printf("failed to setup signed client for %s; ignoring...", f.Knot) 94 - } 95 - } 107 + mergeCheckResponse := s.mergeCheck(f, pull) 96 108 97 109 s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{ 98 110 LoggedInUser: user, ··· 103 115 }) 104 116 } 105 117 118 + func (s *State) mergeCheck(f *FullyResolvedRepo, pull *db.Pull) types.MergeCheckResponse { 119 + if pull.State == db.PullMerged { 120 + return types.MergeCheckResponse{} 121 + } 122 + 123 + secret, err := db.GetRegistrationKey(s.db, f.Knot) 124 + if err != nil { 125 + log.Printf("failed to get registration key: %w", err) 126 + return types.MergeCheckResponse{ 127 + Error: "failed to check merge status: this knot is unregistered", 128 + } 129 + } 130 + 131 + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) 132 + if err != nil { 133 + log.Printf("failed to setup signed client for %s; ignoring: %v", f.Knot, err) 134 + return types.MergeCheckResponse{ 135 + Error: "failed to check merge status", 136 + } 137 + } 138 + 139 + resp, err := ksClient.MergeCheck([]byte(pull.LatestPatch()), pull.OwnerDid, f.RepoName, pull.TargetBranch) 140 + if err != nil { 141 + log.Println("failed to check for mergeability:", err) 142 + switch resp.StatusCode { 143 + case 400: 144 + return types.MergeCheckResponse{ 145 + Error: "failed to check merge status: does this knot support PRs?", 146 + } 147 + default: 148 + return types.MergeCheckResponse{ 149 + Error: "failed to check merge status: this knot is unreachable", 150 + } 151 + } 152 + } 153 + 154 + respBody, err := io.ReadAll(resp.Body) 155 + if err != nil { 156 + log.Println("failed to read merge check response body") 157 + return types.MergeCheckResponse{ 158 + Error: "failed to check merge status: knot is not speaking the right language", 159 + } 160 + } 161 + defer resp.Body.Close() 162 + 163 + var mergeCheckResponse types.MergeCheckResponse 164 + err = json.Unmarshal(respBody, &mergeCheckResponse) 165 + if err != nil { 166 + log.Println("failed to unmarshal merge check response", err) 167 + return types.MergeCheckResponse{ 168 + Error: "failed to check merge status: knot is not speaking the right language", 169 + } 170 + } 171 + 172 + return mergeCheckResponse 173 + } 174 + 106 175 func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) { 107 176 user := s.auth.GetUser(r) 108 177 f, err := fullyResolvedRepo(r) ··· 213 282 return 214 283 } 215 284 285 + roundNumberStr := chi.URLParam(r, "round") 286 + roundNumber, err := strconv.Atoi(roundNumberStr) 287 + if err != nil || roundNumber >= len(pull.Submissions) { 288 + http.Error(w, "bad round id", http.StatusBadRequest) 289 + log.Println("failed to parse round id", err) 290 + return 291 + } 292 + 216 293 switch r.Method { 294 + case http.MethodGet: 295 + s.pages.PullNewCommentFragment(w, pages.PullNewCommentParams{ 296 + LoggedInUser: user, 297 + RepoInfo: f.RepoInfo(s, user), 298 + Pull: pull, 299 + RoundNumber: roundNumber, 300 + }) 301 + return 217 302 case http.MethodPost: 218 303 body := r.FormValue("body") 219 304 if body == "" { 220 305 s.pages.Notice(w, "pull", "Comment body is required") 221 - return 222 - } 223 - 224 - submissionIdstr := r.FormValue("submissionId") 225 - submissionId, err := strconv.Atoi(submissionIdstr) 226 - if err != nil { 227 - s.pages.Notice(w, "pull", "Invalid comment submission.") 228 306 return 229 307 } 230 308 ··· 263 341 }, 264 342 }, 265 343 }) 344 + log.Println(atResp.Uri) 266 345 if err != nil { 267 346 log.Println("failed to create pull comment", err) 268 347 s.pages.Notice(w, "pull-comment", "Failed to create comment.") ··· 276 355 PullId: pull.PullId, 277 356 Body: body, 278 357 CommentAt: atResp.Uri, 279 - SubmissionId: submissionId, 358 + SubmissionId: pull.Submissions[roundNumber].ID, 280 359 }) 281 360 if err != nil { 282 361 log.Println("failed to create pull comment", err) ··· 433 512 } 434 513 435 514 switch r.Method { 515 + case http.MethodGet: 516 + s.pages.PullResubmitFragment(w, pages.PullResubmitParams{ 517 + RepoInfo: f.RepoInfo(s, user), 518 + Pull: pull, 519 + }) 520 + return 436 521 case http.MethodPost: 437 522 patch := r.FormValue("patch") 438 523
+17 -3
appview/state/router.go
··· 66 66 r.Route("/{pull}", func(r chi.Router) { 67 67 r.Use(ResolvePull(s)) 68 68 r.Get("/", s.RepoSinglePull) 69 - r.Get("/round/{round}", s.RepoPullPatch) 69 + 70 + r.Route("/round/{round}", func(r chi.Router) { 71 + r.Get("/", s.RepoPullPatch) 72 + r.Get("/actions", s.PullActions) 73 + r.Route("/comment", func(r chi.Router) { 74 + r.Get("/", s.PullComment) 75 + r.Post("/", s.PullComment) 76 + }) 77 + }) 70 78 71 79 // authorized requests below this point 72 80 r.Group(func(r chi.Router) { 73 81 r.Use(AuthMiddleware(s)) 74 - r.Post("/resubmit", s.ResubmitPull) 75 - r.Post("/comment", s.PullComment) 82 + r.Route("/resubmit", func(r chi.Router) { 83 + r.Get("/", s.ResubmitPull) 84 + r.Post("/", s.ResubmitPull) 85 + }) 86 + r.Route("/comment", func(r chi.Router) { 87 + r.Get("/", s.PullComment) 88 + r.Post("/", s.PullComment) 89 + }) 76 90 r.Post("/close", s.ClosePull) 77 91 r.Post("/reopen", s.ReopenPull) 78 92 // collaborators only