fix(slack): one select instead of four rating buttons
ci / lint-test (push) Successful in 47s

Slack's mobile client gives every button in an actions block its own
full-width row, so the four ratings rendered as four stacked bars and the
nudge filled the screen (user, with a screenshot). A select is one row
and still one decision.

The interactive route now resolves both shapes — a button carries
`value`, a select carries it on `selected_option`. Reading only the
former would have left sharing working while rating silently did
nothing.
This commit is contained in:
2026-07-28 16:22:49 +10:00
parent aaa36dd75e
commit 8fbcbc5f83
2 changed files with 22 additions and 8 deletions
+6 -1
View File
@@ -61,7 +61,12 @@ export async function POST(req: NextRequest) {
if (payload.type !== "block_actions") return new NextResponse(null, { status: 200 }); if (payload.type !== "block_actions") return new NextResponse(null, { status: 200 });
const action = payload.actions?.[0]; const action = payload.actions?.[0];
const [idStr, verb, arg] = String(action?.value ?? "").split(":"); // A button carries `value`; a select carries it on the chosen option. The
// ratings moved to a select because four buttons became four full-width rows
// on Slack mobile, so both shapes have to resolve or rating silently stops
// working while sharing still does.
const rawValue = action?.value ?? action?.selected_option?.value ?? "";
const [idStr, verb, arg] = String(rawValue).split(":");
const transactionId = Number(idStr); const transactionId = Number(idStr);
if (!Number.isInteger(transactionId)) { if (!Number.isInteger(transactionId)) {
return NextResponse.json({ text: "Could not tell which order that was." }); return NextResponse.json({ text: "Could not tell which order that was." });
+13 -4
View File
@@ -69,17 +69,26 @@ export function nudgeBlocks(s: NudgeState) {
}, },
], ],
}, },
// A select, not four buttons. Slack's mobile client gives every button in
// an actions block its own full-width row, so four ratings became four
// stacked bars and the card filled the screen. One select is one row and
// still one decision; the extra tap to open it is the price of a nudge you
// can read at a glance.
{ {
type: "actions", type: "actions",
block_id: "rate", block_id: "rate",
elements: RATINGS.map((r) => ({ elements: [
type: "button", {
action_id: `rate_${r}`, type: "static_select",
action_id: "rate",
placeholder: { type: "plain_text", text: "How was it?" },
options: RATINGS.map((r) => ({
text: { type: "plain_text", text: RATING_LABEL[r] }, text: { type: "plain_text", text: RATING_LABEL[r] },
style: r === "never" ? "danger" : undefined,
value: `${s.transactionId}:rate:${r}`, value: `${s.transactionId}:rate:${r}`,
})), })),
}, },
],
},
{ {
type: "context", type: "context",
elements: [ elements: [