diff --git a/suramadu/web/flare.css b/suramadu/web/flare.css index 57607d3..8d0ef32 100644 --- a/suramadu/web/flare.css +++ b/suramadu/web/flare.css @@ -101,6 +101,21 @@ border-radius: var(--lb-radius); } +/* The board's tags, one checkbox each. A wrapping row, so adding a fifth is + an edit to the list and nothing else. */ +:root .lb-flare-tags { + display: flex; + flex-wrap: wrap; + gap: 4px 14px; + font-size: 13px; +} + +:root .lb-flare-tag { + display: flex; + align-items: center; + cursor: pointer; +} + :root .lb-flare-notice { margin: 0; font-size: 12px; diff --git a/suramadu/web/flare.js b/suramadu/web/flare.js index a596939..58ec87b 100644 --- a/suramadu/web/flare.js +++ b/suramadu/web/flare.js @@ -25,6 +25,10 @@ // shows up on this page. var BOARD_URL = "https://userinput.app/s/did:plc:a2j2g42ai6v65qpbvb6hmubi/3msr5yrvtq22g"; + // What a flare can be tagged with — the board's own list, written down + // rather than fetched. headquarters' report dialog carries the same four. + var TAGS = ["bug", "feature", "question", "thanks"]; + // The lexicon caps an attached image blob at 1,000,000 bytes and the // server re-encodes at the same size, so the target leaves headroom. var IMAGE_BYTE_TARGET = 900000; @@ -116,6 +120,34 @@ reader.readAsDataURL(blob); } + // --- tags -------------------------------------------------------------- + + /* + * One tag, as a checkbox with its label beside it. The label wraps the + * box so the word is part of the hit target and names the control + * without an id for anything to point at. + */ + function tagBox(tag) { + var label = el("label", "lb-flare-tag"); + var box = el("input"); + box.type = "checkbox"; + box.value = tag; + label.appendChild(box); + label.appendChild(document.createTextNode(" " + tag)); + return label; + } + + /* The values of the ticked boxes, in the order the board listed them. */ + function ticked(tagRow) { + var boxes = tagRow.querySelectorAll("input:checked"); + var values = []; + var i; + for (i = 0; i < boxes.length; i++) { + values.push(boxes[i].value); + } + return values; + } + // --- dialog ------------------------------------------------------------ var el = function (tag, className, text) { @@ -166,6 +198,13 @@ body.placeholder = "What were you doing, and what did you expect instead?"; dialog.appendChild(body); + var tagRow = el("div", "lb-flare-tags"); + var t; + for (t = 0; t < TAGS.length; t++) { + tagRow.appendChild(tagBox(TAGS[t])); + } + dialog.appendChild(tagRow); + var attach = el("label", "lb-flare-attach"); var tick = el("input"); tick.type = "checkbox"; @@ -226,7 +265,7 @@ var send = el("button", "lb-flare-send", "Send flare"); send.type = "button"; send.addEventListener("click", function () { - submit(title.value, body.value, tick.checked ? screenshot : null, send, status); + submit(title.value, body.value, ticked(tagRow), tick.checked ? screenshot : null, send, status); }); row.appendChild(send); dialog.appendChild(row); @@ -242,7 +281,7 @@ title.focus(); } - function submit(title, body, shot, send, status) { + function submit(title, body, tags, shot, send, status) { if (send.disabled) { return; } @@ -250,7 +289,7 @@ status.textContent = "Sending…"; status.className = "lb-flare-status"; - var payload = { title: title, body: body }; + var payload = { title: title, body: body, tags: tags }; if (matchId) { payload.matchId = matchId; } diff --git a/tests/shell/test-flare-ui.sh b/tests/shell/test-flare-ui.sh index da35d09..0298457 100755 --- a/tests/shell/test-flare-ui.sh +++ b/tests/shell/test-flare-ui.sh @@ -96,6 +96,13 @@ check "the dialog says a flare posts publicly" \ check "the notice links the board itself" \ grep -q "userinput.app/s/" "$JS" +# The tags are the board's four, written down here and in headquarters' +# report dialog. Both lists have to say the same thing, so this pins the one +# in this file rather than leaving a typo to show up as a tag nothing filters. +check "the dialog offers the board's four tags" bash -c ' + list="$(grep -o "var TAGS = \[[^]]*\]" "$1")" + [ "$list" = "var TAGS = [\"bug\", \"feature\", \"question\", \"thanks\"]" ]' _ "$JS" + # The page's CSP allows img-src data: but not blob:, so an object URL in the # preview renders as a broken image. It shipped that way once. check "the preview never uses an object URL" \