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), | |