max / makenotwork
| 1 | //! The Askama entry point for a described "delete this version" button. |
| 2 | //! |
| 3 | //! Shape 3 step 4 (`108557ec`), the step the plan calls the clean tail: the one |
| 4 | //! part of `static/item-upload.js` that retires with no host remnant. It waited |
| 5 | //! on `27d5e5b8` (a described act inside an Askama page) and on `35756077` (the |
| 6 | //! unlayered `button` rule taking the tone off every described act). Both are |
| 7 | //! settled, so it converts. |
| 8 | //! |
| 9 | //! # What the description says that the markup did not |
| 10 | //! |
| 11 | //! Everything, in this case. There was no markup: the button carried a class |
| 12 | //! and a `data-version-id`, and the whole act lived in a click handler in |
| 13 | //! `item-upload.js` — a `confirm()` string, a `fetch` with CSRF headers, and a |
| 14 | //! `btn.closest('tr').remove()` on success. None of that is legible to anything |
| 15 | //! but a browser. As an [`Act`] it is one sentence: what it is called, where it |
| 16 | //! goes, what it replaces, that it is destructive, and what to ask first. |
| 17 | //! |
| 18 | //! # Why the row id is a parameter, and why the row grew one |
| 19 | //! |
| 20 | //! `DELETE /api/items/{item}/versions/{version}` is a plain API route the |
| 21 | //! description layer does not serve; it answers `200` with an empty body, so it |
| 22 | //! cannot name what it changed and the control has to. See |
| 23 | //! [`Action::replaces`]. |
| 24 | //! |
| 25 | //! The blog pair ([`super::blog_delete_act`]) converted because its templates |
| 26 | //! already gave each row a real id. This one's `<tr>` did not, and the handler |
| 27 | //! it replaces reached the row through `closest('tr')`. So the template grew |
| 28 | //! `id="version-row-{id}"` in the same change. That is worth telling apart from |
| 29 | //! the two rows that are still hand-written: `partials/link_row.html` and |
| 30 | //! `partials/tag.html` target `closest .<class>` on rows whose identity is a |
| 31 | //! CSS class and nothing else, and inventing an id for them would be describing |
| 32 | //! a fact the page does not have. A version row has an id in the database and |
| 33 | //! the template was simply not writing it down. |
| 34 | //! |
| 35 | //! # The download button beside it is sayable now, and was not when this landed |
| 36 | //! |
| 37 | //! Wiki [[mnw-shape-conversion-plans]] Shape 3 step 4 said |
| 38 | //! `download-version-btn` is `Action::get`. When this module landed that was |
| 39 | //! wrong: `GET /api/versions/{id}/download` (`routes::storage::downloads`) |
| 40 | //! answered JSON carrying a presigned URL and the handler navigated to it, so a |
| 41 | //! described `Action::get` would have sent the reader to a JSON body. |
| 42 | //! |
| 43 | //! Fixed 2026-08-26 by the route rather than by the control (`8fc6b1af`, |
| 44 | //! option (a)): it answers **303 to the presigned URL**, so navigating to it is |
| 45 | //! the download and `Action::get` says the truth. The button is a plain |
| 46 | //! navigation in `static/item-upload.js` today and becomes an act whenever the |
| 47 | //! files tab converts. Nothing blocks it any more. |
| 48 | |
| 49 | use Tone; |
| 50 | use Act; |
| 51 | use ; |
| 52 | |
| 53 | /// The delete button for one version's row. |
| 54 | /// |
| 55 | /// Both ids are needed and they are not the same fact: the route is addressed |
| 56 | /// by item and version together, and the row is named by the version alone. |
| 57 | |
| 58 | |
| 59 | Act |
| 60 | new |
| 61 | "Delete", |
| 62 | delete |
| 63 | .replacing, |
| 64 | |
| 65 | .tone |
| 66 | .confirm, |
| 67 | |
| 68 | |
| 69 | |
| 70 | /// The same act as a fragment, for the Askama call site. |
| 71 | |
| 72 | |
| 73 | use Serves as _; |
| 74 | |
| 75 | new.fragment |
| 76 | |
| 77 | |
| 78 | |
| 79 | |
| 80 | /// What the click handler said in four places, said once: where it goes, |
| 81 | /// what it replaces, and what to ask first. |
| 82 | |
| 83 | |
| 84 | let html = html; |
| 85 | |
| 86 | assert! |
| 87 | html.contains, |
| 88 | "{html}" |
| 89 | ; |
| 90 | assert!; |
| 91 | assert! |
| 92 | html.contains, |
| 93 | "{html}" |
| 94 | ; |
| 95 | |
| 96 | |
| 97 | /// The tone. The template said nothing at all here -- the button was |
| 98 | /// `btn-secondary`, so the page did not mark the destructive act even to |
| 99 | /// the stylesheet. |
| 100 | |
| 101 | |
| 102 | let html = html; |
| 103 | |
| 104 | assert!; |
| 105 | |
| 106 | |
| 107 | /// The negative half, as `export_act` asserts it: the dispatcher is gone |
| 108 | /// from this control, not merely bypassed. |
| 109 | |
| 110 | |
| 111 | let html = html; |
| 112 | |
| 113 | assert!; |
| 114 | assert!; |
| 115 | |
| 116 | |
| 117 |