Inline question choices, compact one-click inbox cards, drop snooze
This commit is contained in:
+16
-13
@@ -25,9 +25,6 @@ func (s *Server) ListInbox(w http.ResponseWriter, r *http.Request, params api.Li
|
||||
if params.State != nil {
|
||||
f.State = *params.State
|
||||
}
|
||||
if params.IncludeSnoozed != nil {
|
||||
f.IncludeSnoozed = *params.IncludeSnoozed
|
||||
}
|
||||
|
||||
items, err := s.store.ListInbox(r.Context(), f)
|
||||
if err != nil {
|
||||
@@ -97,10 +94,9 @@ func (s *Server) RespondInbox(w http.ResponseWriter, r *http.Request) {
|
||||
ItemId: item.Id,
|
||||
User: actor(r),
|
||||
Type: create.Type,
|
||||
Option: create.Option,
|
||||
Options: create.Options,
|
||||
Text: create.Text,
|
||||
Attachments: attachments,
|
||||
SnoozeUntil: create.SnoozeUntil,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
},
|
||||
})
|
||||
@@ -155,8 +151,21 @@ func checkResponse(item api.InboxItem, c api.InboxResponseCreate) (api.InboxStat
|
||||
if !question {
|
||||
return bad("only questions have options")
|
||||
}
|
||||
if c.Option == nil || !hasOption(item, *c.Option) {
|
||||
return bad("option must be one of the question's options")
|
||||
if c.Options == nil || len(*c.Options) == 0 {
|
||||
return bad("option needs the chosen options")
|
||||
}
|
||||
if item.Payload["multiple"] != true && len(*c.Options) != 1 {
|
||||
return bad("a pick-one question takes exactly one option")
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, o := range *c.Options {
|
||||
if !hasOption(item, o) {
|
||||
return bad("%q is not one of the question's options", o)
|
||||
}
|
||||
if seen[o] {
|
||||
return bad("%q chosen twice", o)
|
||||
}
|
||||
seen[o] = true
|
||||
}
|
||||
if text != "" {
|
||||
return bad("option takes no text; use other")
|
||||
@@ -172,12 +181,6 @@ func checkResponse(item api.InboxItem, c api.InboxResponseCreate) (api.InboxStat
|
||||
}
|
||||
return api.Answered, nil
|
||||
|
||||
case api.Snooze:
|
||||
if c.SnoozeUntil == nil || !c.SnoozeUntil.After(time.Now()) {
|
||||
return bad("snooze needs a future snooze_until")
|
||||
}
|
||||
return api.Open, nil
|
||||
|
||||
case api.Dismiss:
|
||||
if question {
|
||||
return bad("questions need an option or other, not a dismissal")
|
||||
|
||||
@@ -3,7 +3,6 @@ package server
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"g.fc.run/misc/byre/internal/api"
|
||||
)
|
||||
@@ -15,14 +14,19 @@ func TestCheckResponse(t *testing.T) {
|
||||
State: api.Open,
|
||||
Payload: map[string]any{"options": []any{"red", "blue"}},
|
||||
}
|
||||
checklist := api.InboxItem{
|
||||
Id: "q2",
|
||||
Kind: api.Question,
|
||||
State: api.Open,
|
||||
Payload: map[string]any{"options": []any{"tests", "docs", "lint"}, "multiple": true},
|
||||
}
|
||||
report := api.InboxItem{Id: "r1", Kind: api.Report, State: api.Open, Payload: map[string]any{}}
|
||||
failure := api.InboxItem{Id: "e1", Kind: api.SessionError, State: api.Open, Payload: map[string]any{}}
|
||||
closed := question
|
||||
closed.State = api.Answered
|
||||
|
||||
str := func(s string) *string { return &s }
|
||||
future := time.Now().Add(time.Hour)
|
||||
past := time.Now().Add(-time.Hour)
|
||||
opts := func(s ...string) *[]string { return &s }
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
@@ -31,20 +35,25 @@ func TestCheckResponse(t *testing.T) {
|
||||
state api.InboxState
|
||||
status int
|
||||
}{
|
||||
{"option", question, api.InboxResponseCreate{Type: api.Option, Option: str("blue")}, api.Answered, 0},
|
||||
{"option with text", question, api.InboxResponseCreate{Type: api.Option, Option: str("red"), Text: str("but darker")}, "", http.StatusBadRequest},
|
||||
{"unknown option", question, api.InboxResponseCreate{Type: api.Option, Option: str("green")}, "", http.StatusBadRequest},
|
||||
{"option", question, api.InboxResponseCreate{Type: api.Option, Options: opts("blue")}, api.Answered, 0},
|
||||
{"option with text", question, api.InboxResponseCreate{Type: api.Option, Options: opts("red"), Text: str("but darker")}, "", http.StatusBadRequest},
|
||||
{"unknown option", question, api.InboxResponseCreate{Type: api.Option, Options: opts("green")}, "", http.StatusBadRequest},
|
||||
{"missing option", question, api.InboxResponseCreate{Type: api.Option}, "", http.StatusBadRequest},
|
||||
{"no options", question, api.InboxResponseCreate{Type: api.Option, Options: opts()}, "", http.StatusBadRequest},
|
||||
{"two picks on pick-one", question, api.InboxResponseCreate{Type: api.Option, Options: opts("red", "blue")}, "", http.StatusBadRequest},
|
||||
{"checked", checklist, api.InboxResponseCreate{Type: api.Option, Options: opts("tests", "lint")}, api.Answered, 0},
|
||||
{"checked one", checklist, api.InboxResponseCreate{Type: api.Option, Options: opts("docs")}, api.Answered, 0},
|
||||
{"checked twice", checklist, api.InboxResponseCreate{Type: api.Option, Options: opts("docs", "docs")}, "", http.StatusBadRequest},
|
||||
{"checked unknown", checklist, api.InboxResponseCreate{Type: api.Option, Options: opts("docs", "deploy")}, "", http.StatusBadRequest},
|
||||
{"other", question, api.InboxResponseCreate{Type: api.Other, Text: str("green")}, api.Answered, 0},
|
||||
{"empty other", question, api.InboxResponseCreate{Type: api.Other, Text: str(" ")}, "", http.StatusBadRequest},
|
||||
{"other on error", failure, api.InboxResponseCreate{Type: api.Other, Text: str("retry")}, api.Answered, 0},
|
||||
{"other on report", report, api.InboxResponseCreate{Type: api.Other, Text: str("stop")}, "", http.StatusBadRequest},
|
||||
{"snooze", question, api.InboxResponseCreate{Type: api.Snooze, SnoozeUntil: &future}, api.Open, 0},
|
||||
{"snooze into past", question, api.InboxResponseCreate{Type: api.Snooze, SnoozeUntil: &past}, "", http.StatusBadRequest},
|
||||
{"snooze", question, api.InboxResponseCreate{Type: "snooze"}, "", http.StatusBadRequest},
|
||||
{"dismiss report", report, api.InboxResponseCreate{Type: api.Dismiss}, api.Dismissed, 0},
|
||||
{"dismiss question", question, api.InboxResponseCreate{Type: api.Dismiss}, "", http.StatusBadRequest},
|
||||
{"option on report", report, api.InboxResponseCreate{Type: api.Option, Option: str("red")}, "", http.StatusBadRequest},
|
||||
{"already answered", closed, api.InboxResponseCreate{Type: api.Option, Option: str("red")}, "", http.StatusConflict},
|
||||
{"option on report", report, api.InboxResponseCreate{Type: api.Option, Options: opts("red")}, "", http.StatusBadRequest},
|
||||
{"already answered", closed, api.InboxResponseCreate{Type: api.Option, Options: opts("red")}, "", http.StatusConflict},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
|
||||
Reference in New Issue
Block a user