Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Merge the landing facts into the Review section

ad4d93489a023ec688702a889c53adf4c990dcfa · Keenan Nemetz · 11d ago · parent 59c0625b

Merge the landing facts into the Review section

The merge check and closes links describe the same base/head target the Review controls do, so they now live under Review instead of a separate What would land heading. Changes moves between Review and Approvals, and HTMX write fragments compute the landing facts so they render the complete sidebar.

server/web/request_view.go +34−20

@@ -81,11 +81,26 @@ type requestCI struct {
81 81 CanAllow bool
82 82 }
83 83
84 // requestLandingView holds the landing facts the Review and Changes sections use:
85 // whether the branch still merges, which issues it closes, and how much it
86 // changes. The sidebar embeds it because Review carries the merge check; a write
87 // that renders only the sidebar can leave it zero, and the next full or polling
88 // render fills it in.
89 type requestLandingView struct {
90 FileCount int
91 Added int
92 Removed int
93 AlreadyMerged bool
94 Conflicts []string
95 MergeChecked bool
96 }
97
84 98 // requestSidebarView is the data the change sidebar fragments need. One type for
85 99 // the full page and the HTMX replacements, so an approval or state change can
86 100 // refresh the whole sidebar without rebuilding the rest of the review.
87 101 type requestSidebarView struct {
88 102 Page
103 requestLandingView
89 104 Name string
90 105 Request *metadata.Request
91 106 // Number is the `#42` alias, derived from creation order in history.
@@ -124,22 +139,23 @@ type requestSidebarView struct {
124 139 Error string
125 140 }
126 141
127 // requestLandingView is the "What would land" block: whether the branch still
128 // merges, which issues it closes, and how much it changes. It is split from the
129 // sidebar so the asides of the conversation and Runs tabs can refresh it from
130 // the same computation the full page uses.
131 type requestLandingView struct {
132 FileCount int
133 Added int
134 Removed int
135 AlreadyMerged bool
136 Conflicts []string
137 MergeChecked bool
142 // withLanding returns the same sidebar with the landing facts a full page or a
143 // poll has already computed. The write handlers do not compute them, so their
144 // fragment keeps the section's other state and the next render fills the rest.
145 func (v requestSidebarView) withLanding(l requestLandingView) requestSidebarView {
146 v.requestLandingView = l
147 return v
148 }
149
150 // requestSidebarWithLanding is requestSidebar plus the diff and merge facts the
151 // Review and Changes sections show. Writes use it so an HTMX replacement is the
152 // same complete sidebar a page render gives.
153 func (w *Web) requestSidebarWithLanding(r *http.Request, m *repo.Meta, req *metadata.Request, title, errMsg string) requestSidebarView {
154 return w.requestSidebar(r, m, req, title, errMsg).withLanding(w.requestLanding(m, req))
138 155 }
139 156
140 157 type requestPageView struct {
141 158 requestSidebarView
142 requestLandingView
143 159 Tab string
144 160 Description template.HTML
145 161 CommentTarget *commentTarget
@@ -147,12 +163,11 @@ type requestPageView struct {
147 163 Thread threadView
148 164 }
149 165
150 // requestAsideView is the polling fragment: the landing block and the sidebar
166 // requestAsideView is the polling fragment: the Changes block and the sidebar
151 167 // together, with none of the page shell the handler that serves a full request
152 168 // needs.
153 169 type requestAsideView struct {
154 170 requestSidebarView
155 requestLandingView
156 171 }
157 172
158 173 func (w *Web) requestSidebar(r *http.Request, m *repo.Meta, req *metadata.Request, title, errMsg string) requestSidebarView {
@@ -217,13 +232,13 @@ func (w *Web) requestSidebar(r *http.Request, m *repo.Meta, req *metadata.Reques
217 232 if req.Open() && canMerge(acl, who) {
218 233 // Prepare is what Land runs, so the control and the write agree about
219 234 // approvals, checks and conflicts. A conflict is already called out with its
220 // paths by the "What would land" block, so it does not repeat a sentence here.
235 // paths in the Review section, so it does not repeat a sentence here.
221 236 plan, err := landing.Prepare(w.landGate(m, acl, who), req.ID, who, time.Now().UTC())
222 237 switch {
223 238 case err != nil:
224 239 mergeBlocked = err.Error()
225 240 case len(plan.Conflicts) > 0:
226 mergeBlocked = "" // the "What would land" block already names the paths
241 mergeBlocked = "" // the Review section already names the paths
227 242 default:
228 243 mergeable = true
229 244 }
@@ -328,8 +343,7 @@ func (w *Web) RequestAside(rw http.ResponseWriter, r *http.Request) {
328 343 return
329 344 }
330 345 w.render(rw, "request_aside", requestAsideView{
331 requestSidebarView: w.requestSidebar(r, m, req, "", ""),
332 requestLandingView: w.requestLanding(m, req),
346 requestSidebarView: w.requestSidebar(r, m, req, "", "").withLanding(w.requestLanding(m, req)),
333 347 }, 0)
334 348 }
335 349
@@ -384,10 +398,10 @@ func (w *Web) renderRequestPage(rw http.ResponseWriter, r *http.Request, errMsg
384 398 comments := w.requestComments(m, req)
385 399 attachRequestComments(files, comments)
386 400
387 sidebar := w.requestSidebar(r, m, req, m.Name+" · "+req.Title, errMsg)
401 sidebar := w.requestSidebar(r, m, req, m.Name+" · "+req.Title, errMsg).
402 withLanding(w.landingFrom(m, req, files, alreadyMerged))
388 403 w.render(rw, "request", requestPageView{
389 404 requestSidebarView: sidebar,
390 requestLandingView: w.landingFrom(m, req, files, alreadyMerged),
391 405 Tab: "conversation",
392 406 Description: w.renderMarkdown(m.Name, req.Description),
393 407 CommentTarget: commentTargetFromQuery(r),