Remove legacy question storage #7

Merged
codegirl007 merged 3 commits from remove-legacy-post-storage into app 2026-08-27 16:17:58 +00:00
7 changed files with 116 additions and 4 deletions
Showing only changes of commit eb4cc08a69 - Show all commits
+2
View File
@@ -458,6 +458,8 @@ func TestQuestionPageRendersNestedPostControls(t *testing.T) {
"Shop response",
"Edited",
`action="/posts"`,
`data-submit-once`,
`data-submit-button`,
`action="/posts/` + root.ID + `/edit"`,
`action="/posts/` + homeownerReply.ID + `/edit"`,
`>The model number is 123A.</textarea>`,
+10
View File
@@ -168,6 +168,16 @@ func TestRegisterLoginAsk(t *testing.T) {
if rec.Code != 200 {
t.Fatalf("submit form %d", rec.Code)
}
for _, want := range []string{
`src="/static/app.js"`,
`id="submit-progress"`,
`data-submit-once`,
`data-submit-button`,
} {
if !strings.Contains(rec.Body.String(), want) {
t.Fatalf("submit form missing %q: %s", want, rec.Body.String())
}
}
csrf := csrfFrom(rec.Body.String())
form := strings.NewReader("_csrf=" + csrf + "&title=Leaky+faucet&body=Drip+all+night.&city=Oakland")
req = httptest.NewRequest(http.MethodPost, "/submit", form)
+35
View File
@@ -41,6 +41,31 @@ body {
background-size: 100% 100%, 48px 48px;
}
.submit-progress {
position: fixed;
inset: 0 0 auto;
z-index: 100;
height: 3px;
overflow: hidden;
pointer-events: none;
}
.submit-progress[hidden] { display: none; }
.submit-progress-bar {
display: block;
width: 35%;
height: 100%;
background: var(--signal);
box-shadow: 0 0 12px var(--signal);
animation: submit-progress 900ms ease-in-out infinite;
}
@keyframes submit-progress {
from { transform: translateX(-100%); }
to { transform: translateX(300%); }
}
img, svg { display: block; }
a {
@@ -253,6 +278,12 @@ a {
.btn-primary:hover { filter: brightness(1.08); }
.btn:disabled {
cursor: wait;
opacity: 0.65;
filter: none;
}
.btn-ghost {
background: transparent;
color: var(--ink);
@@ -763,6 +794,10 @@ input:focus, textarea:focus, .btn:focus-visible, .chip:focus-visible, .vote-btn:
@media (prefers-reduced-motion: reduce) {
.btn-primary:hover { filter: none; }
.submit-progress-bar {
width: 100%;
animation: none;
}
}
.admin-users { margin-top: 20px; overflow-x: auto; }
+58
View File
@@ -0,0 +1,58 @@
(() => {
const formSelector = "form[data-submit-once]";
function progressIndicator() {
return document.getElementById("submit-progress");
}
function resetForm(form) {
form.removeAttribute("aria-busy");
delete form.dataset.submitting;
const button = form.querySelector("[data-submit-button]");
if (!button) {
return;
}
button.disabled = false;
button.removeAttribute("aria-disabled");
if (button.dataset.idleLabel) {
button.textContent = button.dataset.idleLabel;
delete button.dataset.idleLabel;
}
}
document.addEventListener("submit", (event) => {
const form = event.target.closest(formSelector);
if (!form) {
return;
}
if (form.dataset.submitting === "true") {
event.preventDefault();
return;
}
form.dataset.submitting = "true";
form.setAttribute("aria-busy", "true");
const button = event.submitter || form.querySelector("[data-submit-button]");
if (button) {
button.dataset.idleLabel = button.textContent;
button.textContent = form.dataset.submittingLabel || "Posting…";
button.disabled = true;
button.setAttribute("aria-disabled", "true");
}
const progress = progressIndicator();
if (progress) {
progress.hidden = false;
}
});
window.addEventListener("pageshow", () => {
document.querySelectorAll(formSelector).forEach(resetForm);
const progress = progressIndicator();
if (progress) {
progress.hidden = true;
}
});
})();
+5
View File
@@ -11,8 +11,13 @@
<link href="https://fonts.googleapis.com/css2?family=Archivo:ital,wght@0,400;0,500;0,600;1,400&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/static/app.css">
<script src="/static/htmx.min.js" defer></script>
<script src="/static/app.js" defer></script>
</head>
<body>
<div id="submit-progress" class="submit-progress" role="progressbar"
aria-label="Posting" aria-valuetext="Posting" hidden>
<span class="submit-progress-bar"></span>
</div>
<a class="skip" href="#main">Skip to content</a>
<header class="top">
<div class="top-inner">
+3 -2
View File
@@ -3,13 +3,14 @@
{{if canReply .User .Root}}
<details class="post-composer">
<summary>Reply</summary>
<form class="post-form" method="post" action="/posts">
<form class="post-form" method="post" action="/posts"
data-submit-once data-submitting-label="Posting…">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
<input type="hidden" name="parent_id" value="{{.Post.ID}}">
<label for="reply-{{.Post.ID}}">Reply to {{.Post.AuthorName}}</label>
<textarea id="reply-{{.Post.ID}}" name="body" rows="5" required maxlength="12000"></textarea>
<div class="post-form-actions">
<button type="submit" class="btn btn-primary">Post reply</button>
<button type="submit" class="btn btn-primary" data-submit-button>Post reply</button>
<button type="reset" class="btn btn-ghost"
onclick="this.closest('details').removeAttribute('open')">Cancel</button>
</div>
+3 -2
View File
@@ -4,7 +4,8 @@
<h1>Ask a question</h1>
<p class="lede">It lands on todays hunt (Pacific time). People vote; the ranking resets at midnight PT.</p>
{{if .Error}}<p class="banner error" role="alert">{{.Error}}</p>{{end}}
<form class="ask" method="post" action="/submit">
<form class="ask" method="post" action="/submit"
data-submit-once data-submitting-label="Posting…">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
<label for="title">Title</label>
<input id="title" name="title" type="text" required maxlength="120" value="{{.TitleVal}}" placeholder="Water heater popping after showers">
@@ -12,7 +13,7 @@
<textarea id="body" name="body" rows="8" required maxlength="8000" placeholder="Age of the house, what you already tried, where you are in the Bay if it helps.">{{.BodyVal}}</textarea>
<label for="city">City <span class="optional">(optional)</span></label>
<input id="city" name="city" type="text" maxlength="80" value="{{.CityVal}}" placeholder="Oakland">
<button type="submit" class="btn btn-primary">Submit to todays hunt</button>
<button type="submit" class="btn btn-primary" data-submit-button>Submit to todays hunt</button>
</form>
</main>
{{template "footer" .}}