feat(app/flags): game data shares
All checks were successful
build / build (push) Successful in 24s
release-tag / release-image (push) Successful in 16m55s

This commit is contained in:
Tijl 2024-08-26 20:28:20 +02:00
parent 5182cf8bfd
commit 84041c6500
Signed by: tijl
GPG Key ID: DAE24BFCD722F053
9 changed files with 276 additions and 65 deletions

View File

@ -5,13 +5,13 @@ import (
"database/sql" "database/sql"
"net/http" "net/http"
"strconv" "strconv"
"time"
"git.tijl.dev/tijl/tijl.dev-core/internal/queries" "git.tijl.dev/tijl/tijl.dev-core/internal/queries"
"git.tijl.dev/tijl/tijl.dev-core/internal/user" "git.tijl.dev/tijl/tijl.dev-core/internal/user"
"git.tijl.dev/tijl/tijl.dev-core/internal/utils" "git.tijl.dev/tijl/tijl.dev-core/internal/utils"
"git.tijl.dev/tijl/tijl.dev-core/modules/db" "git.tijl.dev/tijl/tijl.dev-core/modules/db"
"git.tijl.dev/tijl/tijl.dev-core/modules/i18n" "git.tijl.dev/tijl/tijl.dev-core/modules/i18n"
log "git.tijl.dev/tijl/tijl.dev-core/modules/logger"
"git.tijl.dev/tijl/tijl.dev-core/modules/web" "git.tijl.dev/tijl/tijl.dev-core/modules/web"
"github.com/enescakir/emoji" "github.com/enescakir/emoji"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
@ -29,7 +29,7 @@ func answerHandler(c *fiber.Ctx) error {
answer := c.FormValue("answer") answer := c.FormValue("answer")
gameSession, err := db.Queries.GetFlagsGame(context.TODO(), gameId) gameSession, err := db.Queries.AppFlagsGetGame(context.TODO(), gameId)
if err != nil { if err != nil {
return err return err
} }
@ -39,29 +39,29 @@ func answerHandler(c *fiber.Ctx) error {
return err return err
} }
shuffledCountries := shuffleSlice(countries, gameSession.GameSeed.UUID.String()) shuffledCountries := shuffleSlice(countries, gameSession.GameSeed.String())
correctAnswer := shuffledCountries[gameSession.QuestionCurrent-1] correctAnswer := shuffledCountries[gameSession.QuestionCurrent-1]
if answer == correctAnswer { if answer == correctAnswer {
db.Queries.UpdateFlagsGame(context.TODO(), queries.UpdateFlagsGameParams{ db.Queries.AppFlagsUpdateGame(context.TODO(), queries.AppFlagsUpdateGameParams{
GameID: gameId, GameID: gameId,
QuestionCurrent: gameSession.QuestionCurrent + 1, QuestionCurrent: gameSession.QuestionCurrent + 1,
}) })
db.Queries.UpsertGameAnswer(context.TODO(), queries.UpsertGameAnswerParams{ db.Queries.AppFlagsUpsertGameAnswer(context.TODO(), queries.AppFlagsUpsertGameAnswerParams{
GameID: gameId, GameID: gameId,
Question: gameSession.QuestionCurrent, Question: gameSession.QuestionCurrent,
Errors: 0, Errors: 0,
}) })
} else { } else {
db.Queries.UpsertGameAnswer(context.TODO(), queries.UpsertGameAnswerParams{ db.Queries.AppFlagsUpsertGameAnswer(context.TODO(), queries.AppFlagsUpsertGameAnswerParams{
GameID: gameId, GameID: gameId,
Question: gameSession.QuestionCurrent, Question: gameSession.QuestionCurrent,
Errors: 1, Errors: 1,
}) })
} }
err = db.Queries.UpdateQuestionCorrect(context.TODO(), gameId) err = db.Queries.AppFlagsUpdateQuestionCorrect(context.TODO(), gameId)
if err != nil { if err != nil {
return err return err
} }
@ -87,7 +87,7 @@ func questionHandler(c *fiber.Ctx, newGame NewGameUUID) error {
} }
} }
gameSession, err := db.Queries.GetFlagsGame(context.TODO(), gameId) gameSession, err := db.Queries.AppFlagsGetGame(context.TODO(), gameId)
if err != nil { if err != nil {
return err return err
} }
@ -111,17 +111,20 @@ func questionHandler(c *fiber.Ctx, newGame NewGameUUID) error {
return gameEndHandler(c) return gameEndHandler(c)
} }
shuffledCountries := shuffleSlice(countries, gameSession.GameSeed.UUID.String()) shuffledCountries := shuffleSlice(countries, gameSession.GameSeed.String())
shuffledAnswers := shuffleSlice(countries, gameSession.GameSeed.UUID.String()+string(gameSession.QuestionCurrent)) shuffledAnswers := shuffleSlice(countries, gameSession.GameSeed.String()+string(gameSession.QuestionCurrent))
shuffledAnswers = shuffledAnswers[0:4] // 4 random aswers shuffledAnswers = shuffledAnswers[0:4] // 4 random aswers
shuffledAnswers = append(shuffledAnswers, shuffledCountries[gameSession.QuestionCurrent-1]) // add correct answer shuffledAnswers = append(shuffledAnswers, shuffledCountries[gameSession.QuestionCurrent-1]) // add correct answer
shuffledAnswers = shuffleSlice(shuffledAnswers, gameSession.GameSeed.UUID.String()+string(gameSession.QuestionCurrent)) // shuffle again shuffledAnswers = shuffleSlice(shuffledAnswers, gameSession.GameSeed.String()+string(gameSession.QuestionCurrent)) // shuffle again
if gameSession.QuestionAmount != 0 && int(gameSession.QuestionAmount) < len(countries) { if gameSession.QuestionAmount != 0 && int(gameSession.QuestionAmount) < len(countries) {
shuffledCountries = shuffledCountries[0:gameSession.QuestionAmount] shuffledCountries = shuffledCountries[0:gameSession.QuestionAmount]
} }
log.Debug().Interface("shuffledCountries", shuffledCountries).Interface("shuffledAnswers", shuffledAnswers).Msg("data") var timeleft = []string{}
if gameSession.Seconds != 0 {
timeleft = append(timeleft, strconv.Itoa(int(gameSession.Seconds)-int(time.Since(gameSession.CreatedAt).Seconds())))
}
flag, err := emoji.CountryFlag(shuffledCountries[gameSession.QuestionCurrent-1]) flag, err := emoji.CountryFlag(shuffledCountries[gameSession.QuestionCurrent-1])
if err != nil { if err != nil {
@ -130,13 +133,23 @@ func questionHandler(c *fiber.Ctx, newGame NewGameUUID) error {
data := *web.Common(c) data := *web.Common(c)
data["Title"] = "tmp" data["Title"] = "tmp"
data["TimeLeft"] = timeleft
data["QuestionsLeft"] = len(shuffledCountries) - int(gameSession.QuestionCurrent) + 1
data["Answers"] = shuffledAnswers data["Answers"] = shuffledAnswers
data["Flag"] = flag data["Flag"] = flag
data["Errors"] = gameSession.QuestionsErrors data["Errors"] = gameSession.QuestionsErrors
return c.Render("apps/flags/question", data, "layouts/base") return c.Render("apps/flags/question", data, "layouts/base")
} }
func getGameData() { func sharedGameHandler(c *fiber.Ctx) error {
shareKey := c.FormValue("sharekey")
data, err := db.Queries.AppFlagsGetSharedData(context.TODO(), shareKey)
if err != nil {
return err
}
return setupGame(c, data.Tags, int(data.Questions), int(data.Seconds), true, data.GameSeed)
} }
func startNewGameHandler(c *fiber.Ctx) error { func startNewGameHandler(c *fiber.Ctx) error {
@ -164,6 +177,37 @@ func startNewGameHandler(c *fiber.Ctx) error {
return err return err
} }
seconds, err := strconv.Atoi(c.FormValue("seconds"))
if err != nil {
return err
}
if c.FormValue("share") != "" {
return createSharedGameData(c, selectedTags, maxQuestions, seconds)
}
return setupGame(c, selectedTags, maxQuestions, seconds, false, uuid.UUID{})
}
func createSharedGameData(c *fiber.Ctx, tags []string, maxQuestions int, seconds int) error {
shareKey := utils.RandString(4)
_, err := db.Queries.AppFlagsNewSharedData(context.TODO(), queries.AppFlagsNewSharedDataParams{
ShareKey: shareKey,
Tags: tags,
Questions: int32(maxQuestions),
Seconds: int32(seconds),
})
if err != nil {
return err
}
data := *web.Common(c)
data["Title"] = "tmp"
data["ShareKey"] = shareKey
return c.Render("apps/flags/shared", data, "layouts/base")
}
func setupGame(c *fiber.Ctx, tags []string, maxQuestions int, seconds int, useGameSeed bool, gameSeed uuid.UUID) error {
var Quid = sql.NullString{} var Quid = sql.NullString{}
uid, err := user.GetSession(c) uid, err := user.GetSession(c)
if err == nil { if err == nil {
@ -171,22 +215,30 @@ func startNewGameHandler(c *fiber.Ctx) error {
Quid.String = uid Quid.String = uid
} }
row, err := db.Queries.CreateFlagsGame(context.TODO(), queries.CreateFlagsGameParams{ createGameParams := queries.AppFlagsCreateGameWithSeedParams{
Uid: Quid, Uid: Quid,
Tags: selectedTags, Tags: tags,
QuestionAmount: int32(maxQuestions), QuestionAmount: int32(maxQuestions),
}) Seconds: int32(seconds),
GameSeed: uuid.New(),
}
if useGameSeed {
createGameParams.GameSeed = gameSeed
}
gameID, err := db.Queries.AppFlagsCreateGameWithSeed(context.TODO(), createGameParams)
if err != nil { if err != nil {
return err return err
} }
c.Cookie(&fiber.Cookie{ c.Cookie(&fiber.Cookie{
Name: flagSessionCookie, Name: flagSessionCookie,
Value: row.GameID.String(), Value: gameID.String(),
//Secure: true, //Secure: true,
}) })
return questionHandler(c, NewGameUUID{used: true, UUID: row.GameID}) return questionHandler(c, NewGameUUID{used: true, UUID: gameID})
} }
func gameEndHandler(c *fiber.Ctx) error { func gameEndHandler(c *fiber.Ctx) error {
@ -194,7 +246,7 @@ func gameEndHandler(c *fiber.Ctx) error {
if err != nil { if err != nil {
return err return err
} }
gameSession, err := db.Queries.GetFlagsGame(context.TODO(), gameId) gameSession, err := db.Queries.AppFlagsGetGame(context.TODO(), gameId)
if err != nil { if err != nil {
return err return err
} }

View File

@ -51,6 +51,8 @@ func Setup() {
return answerHandler(c) return answerHandler(c)
case "exit": case "exit":
return stopGameHandler(c) return stopGameHandler(c)
case "shared":
return sharedGameHandler(c)
} }
return nil return nil

View File

@ -1,28 +1,39 @@
-- name: CreateFlagsGame :one -- name: AppFlagsCreateGame :one
INSERT INTO app_flags_games (uid, tags, question_amount) INSERT INTO app_flags_games (uid, tags, question_amount, seconds)
VALUES ($1, $2, $3) VALUES ($1, $2, $3, $4)
RETURNING game_id, game_seed; RETURNING game_id, game_seed;
-- name: GetFlagsGame :one -- name: AppFlagsCreateGameWithSeed :one
INSERT INTO app_flags_games (uid, tags, question_amount, seconds, game_seed)
VALUES ($1, $2, $3, $4, $5)
RETURNING game_id;
-- name: AppFlagsGetGame :one
SELECT * FROM app_flags_games WHERE game_id = $1 LIMIT 1; SELECT * FROM app_flags_games WHERE game_id = $1 LIMIT 1;
-- name: UpdateFlagsGame :exec -- name: AppFlagsUpdateGame :exec
UPDATE app_flags_games UPDATE app_flags_games
SET question_current = $1, last_activity = CURRENT_TIMESTAMP SET question_current = $1, last_activity = CURRENT_TIMESTAMP
WHERE game_id = $2; WHERE game_id = $2;
-- name: UpdateQuestionCorrect :exec -- name: AppFlagsUpdateQuestionCorrect :exec
UPDATE app_flags_games UPDATE app_flags_games
SET questions_errors = ( SET questions_errors = (
SELECT COALESCE(SUM(errors), 0) SELECT COALESCE(SUM(errors), 0)
FROM app_flags_games_answers FROM app_flags_games_answers
WHERE app_flags_games_answers.game_id = app_flags_games.game_id WHERE app_flags_games_answers.game_id = app_flags_games.game_id
) ), last_activity = CURRENT_TIMESTAMP
WHERE app_flags_games.game_id = $1; WHERE app_flags_games.game_id = $1;
-- name: UpsertGameAnswer :exec -- name: AppFlagsUpsertGameAnswer :exec
INSERT INTO app_flags_games_answers (game_id, question, errors) INSERT INTO app_flags_games_answers (game_id, question, errors)
VALUES ($1, $2, $3) VALUES ($1, $2, $3)
ON CONFLICT (game_id, question) ON CONFLICT (game_id, question)
DO UPDATE SET errors = app_flags_games_answers.errors + EXCLUDED.errors; DO UPDATE SET errors = app_flags_games_answers.errors + EXCLUDED.errors;
-- name: AppFlagsNewSharedData :one
INSERT INTO app_flags_games_shared_data (share_key, game_seed, tags, questions, seconds) VALUES ($1, $2, $3, $4, $5) RETURNING game_seed;
-- name: AppFlagsGetSharedData :one
SELECT * FROM app_flags_games_shared_data WHERE share_key = $1 LIMIT 1;

View File

@ -13,42 +13,76 @@ import (
"github.com/lib/pq" "github.com/lib/pq"
) )
const createFlagsGame = `-- name: CreateFlagsGame :one const appFlagsCreateGame = `-- name: AppFlagsCreateGame :one
INSERT INTO app_flags_games (uid, tags, question_amount) INSERT INTO app_flags_games (uid, tags, question_amount, seconds)
VALUES ($1, $2, $3) VALUES ($1, $2, $3, $4)
RETURNING game_id, game_seed RETURNING game_id, game_seed
` `
type CreateFlagsGameParams struct { type AppFlagsCreateGameParams struct {
Uid sql.NullString Uid sql.NullString
Tags []string Tags []string
QuestionAmount int32 QuestionAmount int32
Seconds int32
} }
type CreateFlagsGameRow struct { type AppFlagsCreateGameRow struct {
GameID uuid.UUID GameID uuid.UUID
GameSeed uuid.NullUUID GameSeed uuid.UUID
} }
func (q *Queries) CreateFlagsGame(ctx context.Context, arg CreateFlagsGameParams) (CreateFlagsGameRow, error) { func (q *Queries) AppFlagsCreateGame(ctx context.Context, arg AppFlagsCreateGameParams) (AppFlagsCreateGameRow, error) {
row := q.db.QueryRowContext(ctx, createFlagsGame, arg.Uid, pq.Array(arg.Tags), arg.QuestionAmount) row := q.db.QueryRowContext(ctx, appFlagsCreateGame,
var i CreateFlagsGameRow arg.Uid,
pq.Array(arg.Tags),
arg.QuestionAmount,
arg.Seconds,
)
var i AppFlagsCreateGameRow
err := row.Scan(&i.GameID, &i.GameSeed) err := row.Scan(&i.GameID, &i.GameSeed)
return i, err return i, err
} }
const getFlagsGame = `-- name: GetFlagsGame :one const appFlagsCreateGameWithSeed = `-- name: AppFlagsCreateGameWithSeed :one
SELECT game_id, game_seed, uid, tags, question_amount, question_current, questions_errors, created_at, last_activity FROM app_flags_games WHERE game_id = $1 LIMIT 1 INSERT INTO app_flags_games (uid, tags, question_amount, seconds, game_seed)
VALUES ($1, $2, $3, $4, $5)
RETURNING game_id
` `
func (q *Queries) GetFlagsGame(ctx context.Context, gameID uuid.UUID) (AppFlagsGame, error) { type AppFlagsCreateGameWithSeedParams struct {
row := q.db.QueryRowContext(ctx, getFlagsGame, gameID) Uid sql.NullString
Tags []string
QuestionAmount int32
Seconds int32
GameSeed uuid.UUID
}
func (q *Queries) AppFlagsCreateGameWithSeed(ctx context.Context, arg AppFlagsCreateGameWithSeedParams) (uuid.UUID, error) {
row := q.db.QueryRowContext(ctx, appFlagsCreateGameWithSeed,
arg.Uid,
pq.Array(arg.Tags),
arg.QuestionAmount,
arg.Seconds,
arg.GameSeed,
)
var game_id uuid.UUID
err := row.Scan(&game_id)
return game_id, err
}
const appFlagsGetGame = `-- name: AppFlagsGetGame :one
SELECT game_id, game_seed, uid, tags, seconds, question_amount, question_current, questions_errors, created_at, last_activity FROM app_flags_games WHERE game_id = $1 LIMIT 1
`
func (q *Queries) AppFlagsGetGame(ctx context.Context, gameID uuid.UUID) (AppFlagsGame, error) {
row := q.db.QueryRowContext(ctx, appFlagsGetGame, gameID)
var i AppFlagsGame var i AppFlagsGame
err := row.Scan( err := row.Scan(
&i.GameID, &i.GameID,
&i.GameSeed, &i.GameSeed,
&i.Uid, &i.Uid,
pq.Array(&i.Tags), pq.Array(&i.Tags),
&i.Seconds,
&i.QuestionAmount, &i.QuestionAmount,
&i.QuestionCurrent, &i.QuestionCurrent,
&i.QuestionsErrors, &i.QuestionsErrors,
@ -58,51 +92,95 @@ func (q *Queries) GetFlagsGame(ctx context.Context, gameID uuid.UUID) (AppFlagsG
return i, err return i, err
} }
const updateFlagsGame = `-- name: UpdateFlagsGame :exec const appFlagsGetSharedData = `-- name: AppFlagsGetSharedData :one
SELECT id, share_key, game_seed, questions, tags, seconds, created_at FROM app_flags_games_shared_data WHERE share_key = $1 LIMIT 1
`
func (q *Queries) AppFlagsGetSharedData(ctx context.Context, shareKey string) (AppFlagsGamesSharedDatum, error) {
row := q.db.QueryRowContext(ctx, appFlagsGetSharedData, shareKey)
var i AppFlagsGamesSharedDatum
err := row.Scan(
&i.ID,
&i.ShareKey,
&i.GameSeed,
&i.Questions,
pq.Array(&i.Tags),
&i.Seconds,
&i.CreatedAt,
)
return i, err
}
const appFlagsNewSharedData = `-- name: AppFlagsNewSharedData :one
INSERT INTO app_flags_games_shared_data (share_key, game_seed, tags, questions, seconds) VALUES ($1, $2, $3, $4, $5) RETURNING game_seed
`
type AppFlagsNewSharedDataParams struct {
ShareKey string
GameSeed uuid.UUID
Tags []string
Questions int32
Seconds int32
}
func (q *Queries) AppFlagsNewSharedData(ctx context.Context, arg AppFlagsNewSharedDataParams) (uuid.UUID, error) {
row := q.db.QueryRowContext(ctx, appFlagsNewSharedData,
arg.ShareKey,
arg.GameSeed,
pq.Array(arg.Tags),
arg.Questions,
arg.Seconds,
)
var game_seed uuid.UUID
err := row.Scan(&game_seed)
return game_seed, err
}
const appFlagsUpdateGame = `-- name: AppFlagsUpdateGame :exec
UPDATE app_flags_games UPDATE app_flags_games
SET question_current = $1, last_activity = CURRENT_TIMESTAMP SET question_current = $1, last_activity = CURRENT_TIMESTAMP
WHERE game_id = $2 WHERE game_id = $2
` `
type UpdateFlagsGameParams struct { type AppFlagsUpdateGameParams struct {
QuestionCurrent int32 QuestionCurrent int32
GameID uuid.UUID GameID uuid.UUID
} }
func (q *Queries) UpdateFlagsGame(ctx context.Context, arg UpdateFlagsGameParams) error { func (q *Queries) AppFlagsUpdateGame(ctx context.Context, arg AppFlagsUpdateGameParams) error {
_, err := q.db.ExecContext(ctx, updateFlagsGame, arg.QuestionCurrent, arg.GameID) _, err := q.db.ExecContext(ctx, appFlagsUpdateGame, arg.QuestionCurrent, arg.GameID)
return err return err
} }
const updateQuestionCorrect = `-- name: UpdateQuestionCorrect :exec const appFlagsUpdateQuestionCorrect = `-- name: AppFlagsUpdateQuestionCorrect :exec
UPDATE app_flags_games UPDATE app_flags_games
SET questions_errors = ( SET questions_errors = (
SELECT COALESCE(SUM(errors), 0) SELECT COALESCE(SUM(errors), 0)
FROM app_flags_games_answers FROM app_flags_games_answers
WHERE app_flags_games_answers.game_id = app_flags_games.game_id WHERE app_flags_games_answers.game_id = app_flags_games.game_id
) ), last_activity = CURRENT_TIMESTAMP
WHERE app_flags_games.game_id = $1 WHERE app_flags_games.game_id = $1
` `
func (q *Queries) UpdateQuestionCorrect(ctx context.Context, gameID uuid.UUID) error { func (q *Queries) AppFlagsUpdateQuestionCorrect(ctx context.Context, gameID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, updateQuestionCorrect, gameID) _, err := q.db.ExecContext(ctx, appFlagsUpdateQuestionCorrect, gameID)
return err return err
} }
const upsertGameAnswer = `-- name: UpsertGameAnswer :exec const appFlagsUpsertGameAnswer = `-- name: AppFlagsUpsertGameAnswer :exec
INSERT INTO app_flags_games_answers (game_id, question, errors) INSERT INTO app_flags_games_answers (game_id, question, errors)
VALUES ($1, $2, $3) VALUES ($1, $2, $3)
ON CONFLICT (game_id, question) ON CONFLICT (game_id, question)
DO UPDATE SET errors = app_flags_games_answers.errors + EXCLUDED.errors DO UPDATE SET errors = app_flags_games_answers.errors + EXCLUDED.errors
` `
type UpsertGameAnswerParams struct { type AppFlagsUpsertGameAnswerParams struct {
GameID uuid.UUID GameID uuid.UUID
Question int32 Question int32
Errors int32 Errors int32
} }
func (q *Queries) UpsertGameAnswer(ctx context.Context, arg UpsertGameAnswerParams) error { func (q *Queries) AppFlagsUpsertGameAnswer(ctx context.Context, arg AppFlagsUpsertGameAnswerParams) error {
_, err := q.db.ExecContext(ctx, upsertGameAnswer, arg.GameID, arg.Question, arg.Errors) _, err := q.db.ExecContext(ctx, appFlagsUpsertGameAnswer, arg.GameID, arg.Question, arg.Errors)
return err return err
} }

View File

@ -13,9 +13,10 @@ import (
type AppFlagsGame struct { type AppFlagsGame struct {
GameID uuid.UUID GameID uuid.UUID
GameSeed uuid.NullUUID GameSeed uuid.UUID
Uid sql.NullString Uid sql.NullString
Tags []string Tags []string
Seconds int32
QuestionAmount int32 QuestionAmount int32
QuestionCurrent int32 QuestionCurrent int32
QuestionsErrors int32 QuestionsErrors int32
@ -24,9 +25,20 @@ type AppFlagsGame struct {
} }
type AppFlagsGamesAnswer struct { type AppFlagsGamesAnswer struct {
GameID uuid.UUID GameID uuid.UUID
Question int32 Question int32
Errors int32 Errors int32
CreatedAt time.Time
}
type AppFlagsGamesSharedDatum struct {
ID int32
ShareKey string
GameSeed uuid.UUID
Questions int32
Tags []string
Seconds int32
CreatedAt time.Time
} }
type Session struct { type Session struct {

View File

@ -1,8 +1,9 @@
CREATE TABLE app_flags_games ( CREATE TABLE app_flags_games (
game_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), game_id UUID PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
game_seed UUID DEFAULT gen_random_uuid(), game_seed UUID NOT NULL DEFAULT gen_random_uuid(),
uid VARCHAR DEFAULT NULL, uid VARCHAR DEFAULT NULL,
tags VARCHAR[] NOT NULL, tags VARCHAR[] NOT NULL,
seconds INT NOT NULL,
question_amount INT NOT NULL, question_amount INT NOT NULL,
question_current INT DEFAULT 1 NOT NULL, question_current INT DEFAULT 1 NOT NULL,
questions_errors INT DEFAULT 0 NOT NULL, questions_errors INT DEFAULT 0 NOT NULL,
@ -11,10 +12,21 @@ CREATE TABLE app_flags_games (
FOREIGN KEY (uid) REFERENCES users (uid) FOREIGN KEY (uid) REFERENCES users (uid)
); );
CREATE TABLE app_flags_games_shared_data (
id SERIAL PRIMARY KEY,
share_key VARCHAR NOT NULL,
game_seed UUID NOT NULL DEFAULT gen_random_uuid(),
questions INT DEFAULT 0 NOT NULL,
tags VARCHAR[] NOT NULL,
seconds INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
CREATE TABLE app_flags_games_answers ( CREATE TABLE app_flags_games_answers (
game_id UUID NOT NULL, game_id UUID NOT NULL,
question INT NOT NULL, question INT NOT NULL,
errors INT NOT NULL, errors INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (game_id) REFERENCES app_flags_games (game_id), FOREIGN KEY (game_id) REFERENCES app_flags_games (game_id),
CONSTRAINT app_flags_games_answers_unique UNIQUE (game_id, question) CONSTRAINT app_flags_games_answers_unique UNIQUE (game_id, question)
); );

View File

@ -2,9 +2,17 @@
<div> <div>
<div>
Questions left: {{.QuestionsLeft}}
</div>
<div> <div>
Errors: {{.Errors}} Errors: {{.Errors}}
</div> </div>
{{range .TimeLeft}}
<div>
Seconds left: <span id="countdown">{{.}}</span>
</div>
{{end}}
<div class="flex justify-center p-4 mt-4"> <div class="flex justify-center p-4 mt-4">
<span style="font-size: 105px;">{{.Flag}}</span> <span style="font-size: 105px;">{{.Flag}}</span>
@ -28,3 +36,18 @@
</div> </div>
</div> </div>
{{range .TimeLeft}}
<script>
function startCountdown() {
const countdownElement = document.getElementById('countdown');
let countdownNumber = parseInt(countdownElement.innerHTML, 10);
const intervalId = setInterval(() => {
countdownNumber -= 1;
countdownElement.innerHTML = countdownNumber;
}, 1000);
}
startCountdown();
</script>
{{end}}

View File

@ -0,0 +1 @@
<div>{{.ShareKey}}</div>

View File

@ -14,12 +14,32 @@
{{end}} {{end}}
</div> </div>
<!--<label class="cursor-pointer label"> <label class="cursor-pointer label mt-5">
<span class="label-text">Aantal vragen</span> <span class="label-text">Share</span>
<input name="tags" value="{{.}}" type="checkbox" class="checkbox checkbox-primary" /> <input name="share" type="checkbox" class="checkbox checkbox-primary" />
</label>--> </label>
<div>
<label>
<span>max questions</span>
<input value="0" placeholder="max questions" name="max_questions" type="number"
class="input input-bordered" />
</label>
</div>
<div>
<label>
<span>time limit</span>
<input value="0" placeholder="seconds time limit" name="seconds" type="number"
class="input input-bordered" />
</label>
</div>
<input value="0" placeholder="max questions" name="max_questions" type="number" class="input input-bordered" />
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary">Submit</button>
</form> </form>
<form class="mt-4" method="post">
<input type="text" class="hidden" name="type" value="shared" />
<input type="text" class="input input-bordered" name="sharekey" placeholder="sharekey" />
<button type="submit" class="btn btn-primary">Shared</button>
</form>
</div> </div>