feat: initial commit
This commit is contained in:
@ -3,14 +3,17 @@ package quiz
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
httpCtx "forge.cadoles.com/wpetit/kouiz/internal/http/context"
|
||||
"forge.cadoles.com/wpetit/kouiz/internal/http/handler/webui/auth"
|
||||
"forge.cadoles.com/wpetit/kouiz/internal/http/handler/webui/common"
|
||||
"forge.cadoles.com/wpetit/kouiz/internal/http/handler/webui/quiz/component"
|
||||
"forge.cadoles.com/wpetit/kouiz/internal/store"
|
||||
"github.com/a-h/templ"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func (h *Handler) getQuizPage(w http.ResponseWriter, r *http.Request) {
|
||||
@ -20,8 +23,8 @@ func (h *Handler) getQuizPage(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
issue := component.QuizPage(*vmodel)
|
||||
templ.Handler(issue).ServeHTTP(w, r)
|
||||
quizPage := component.QuizPage(*vmodel)
|
||||
templ.Handler(quizPage).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (h *Handler) fillQuizPageVModel(r *http.Request) (*component.QuizPageVModel, error) {
|
||||
@ -40,15 +43,8 @@ func (h *Handler) fillQuizPageVModel(r *http.Request) (*component.QuizPageVModel
|
||||
}
|
||||
|
||||
func (h *Handler) fillQuizPagePlayer(ctx context.Context, vmodel *component.QuizPageVModel, r *http.Request) error {
|
||||
user := auth.ContextUser(ctx)
|
||||
|
||||
player := &store.Player{
|
||||
Name: user.Name,
|
||||
UserID: user.ID,
|
||||
UserProvider: user.Provider,
|
||||
}
|
||||
|
||||
if err := h.store.UpsertPlayer(ctx, player); err != nil {
|
||||
player, err := h.getRequestPlayer(r)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
@ -58,20 +54,38 @@ func (h *Handler) fillQuizPagePlayer(ctx context.Context, vmodel *component.Quiz
|
||||
}
|
||||
|
||||
func (h *Handler) fillQuizPageTurn(ctx context.Context, vmodel *component.QuizPageVModel, r *http.Request) error {
|
||||
turn, err := h.store.GetQuizTurn(ctx, h.playInterval)
|
||||
turn, err := h.store.GetQuizTurn(ctx, h.playInterval, h.playPeriod)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
spew.Dump(turn)
|
||||
vmodel.CurrentTurn = turn
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Handler) handleQuizForm(w http.ResponseWriter, r *http.Request) {
|
||||
quizForm := component.NewQuizForm()
|
||||
func (h *Handler) getRequestPlayer(r *http.Request) (*store.Player, error) {
|
||||
ctx := r.Context()
|
||||
user := auth.ContextUser(ctx)
|
||||
|
||||
if err := quizForm.Handle(r); err != nil {
|
||||
player, err := h.store.UpsertPlayer(ctx, user.Name, user.Email, user.Provider)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return player, nil
|
||||
}
|
||||
|
||||
func (h *Handler) handleSelectEntryForm(w http.ResponseWriter, r *http.Request) {
|
||||
player, err := h.getRequestPlayer(r)
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
selectEntryForm := component.NewSelectEntryForm()
|
||||
|
||||
if err := selectEntryForm.Handle(r); err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
@ -82,15 +96,166 @@ func (h *Handler) handleQuizForm(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if errs := quizForm.Validate(); errs != nil {
|
||||
if errs := selectEntryForm.Validate(); errs != nil {
|
||||
page := component.QuizPage(*vmodel)
|
||||
templ.Handler(page).ServeHTTP(w, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
page := component.QuizPage(*vmodel)
|
||||
templ.Handler(page).ServeHTTP(w, r)
|
||||
rawSelectedEntry, exists := selectEntryForm.Field("entry").Get("value")
|
||||
if !exists {
|
||||
page := component.QuizPage(*vmodel)
|
||||
templ.Handler(page).ServeHTTP(w, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
selectedEntry, err := strconv.ParseUint(rawSelectedEntry.(string), 10, 64)
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
turn, err := h.store.GetQuizTurn(ctx, h.playInterval, h.playPeriod)
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
err = h.store.Do(ctx, func(db *gorm.DB) error {
|
||||
err := db.Model(&player).Updates(map[string]any{
|
||||
"selected_at": time.Now().UTC(),
|
||||
"selected_entry": &selectedEntry,
|
||||
"selected_turn": turn.ID,
|
||||
}).Error
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
baseURL := httpCtx.BaseURL(ctx)
|
||||
|
||||
http.Redirect(w, r, baseURL.String(), http.StatusTemporaryRedirect)
|
||||
}
|
||||
|
||||
func (h *Handler) handleAnswerForm(w http.ResponseWriter, r *http.Request) {
|
||||
player, err := h.getRequestPlayer(r)
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
if player.SelectedEntry == nil {
|
||||
h.handleError(w, r, errors.New("missing player selected entry"))
|
||||
return
|
||||
}
|
||||
|
||||
answerForm := component.NewAnswerForm()
|
||||
|
||||
if err := answerForm.Handle(r); err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
vmodel, err := h.fillQuizPageVModel(r)
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
if errs := answerForm.Validate(); errs != nil {
|
||||
page := component.QuizPage(*vmodel)
|
||||
templ.Handler(page).ServeHTTP(w, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
rawAnswerIndex, exists := answerForm.Field("answer").Get("value")
|
||||
if !exists {
|
||||
page := component.QuizPage(*vmodel)
|
||||
templ.Handler(page).ServeHTTP(w, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
selectedAnswerIndex, err := strconv.ParseInt(rawAnswerIndex.(string), 10, 64)
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
turn, err := h.store.GetQuizTurn(ctx, h.playInterval, h.playPeriod)
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
var selectedEntry *store.QuizEntry
|
||||
for _, e := range turn.Entries {
|
||||
if *player.SelectedEntry != e.ID {
|
||||
continue
|
||||
}
|
||||
|
||||
selectedEntry = e
|
||||
break
|
||||
}
|
||||
|
||||
if selectedEntry == nil {
|
||||
h.handleError(w, r, errors.Errorf("invalid selected entry '%d'", *player.SelectedEntry))
|
||||
return
|
||||
}
|
||||
|
||||
if selectedAnswerIndex < 0 || int(selectedAnswerIndex) >= len(selectedEntry.Propositions) {
|
||||
h.handleError(w, r, errors.Errorf("invalid selected answer index '%d'", selectedAnswerIndex))
|
||||
return
|
||||
}
|
||||
|
||||
selectedAnswer := selectedEntry.Propositions[selectedAnswerIndex]
|
||||
|
||||
won := selectedAnswer == selectedEntry.Answer
|
||||
|
||||
err = h.store.Tx(ctx, func(db *gorm.DB) error {
|
||||
newScore := player.Score
|
||||
if won {
|
||||
newScore += 2 * int(selectedEntry.Level+1)
|
||||
}
|
||||
|
||||
result := db.Model(&player).Updates(map[string]any{
|
||||
"score": newScore,
|
||||
"selected_answer": selectedAnswerIndex,
|
||||
"played_at": time.Now().UTC(),
|
||||
}).Where("played_at = ?", player.PlayedAt)
|
||||
|
||||
if result.Error != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
if result.RowsAffected != 1 {
|
||||
return errors.New("unexpected number of updated player")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
baseURL := httpCtx.BaseURL(ctx)
|
||||
baseURL = baseURL.JoinPath("/quiz/result")
|
||||
|
||||
http.Redirect(w, r, baseURL.String(), http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (h *Handler) handleError(w http.ResponseWriter, r *http.Request, err error) {
|
||||
|
Reference in New Issue
Block a user