feat: persist chat messages to SQLite (#1)
All checks were successful
Deploy c4 / deploy (push) Successful in 44s
All checks were successful
Deploy c4 / deploy (push) Successful in 44s
This commit was merged in pull request #1.
This commit is contained in:
71
db/gen/chat.sql.go
Normal file
71
db/gen/chat.sql.go
Normal file
@@ -0,0 +1,71 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: chat.sql
|
||||
|
||||
package gen
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createChatMessage = `-- name: CreateChatMessage :exec
|
||||
INSERT INTO chat_messages (game_id, nickname, color, message, created_at)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
`
|
||||
|
||||
type CreateChatMessageParams struct {
|
||||
GameID string
|
||||
Nickname string
|
||||
Color int64
|
||||
Message string
|
||||
CreatedAt int64
|
||||
}
|
||||
|
||||
func (q *Queries) CreateChatMessage(ctx context.Context, arg CreateChatMessageParams) error {
|
||||
_, err := q.db.ExecContext(ctx, createChatMessage,
|
||||
arg.GameID,
|
||||
arg.Nickname,
|
||||
arg.Color,
|
||||
arg.Message,
|
||||
arg.CreatedAt,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const getChatMessages = `-- name: GetChatMessages :many
|
||||
SELECT id, game_id, nickname, color, message, created_at FROM chat_messages
|
||||
WHERE game_id = ?
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT 50
|
||||
`
|
||||
|
||||
func (q *Queries) GetChatMessages(ctx context.Context, gameID string) ([]ChatMessage, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getChatMessages, gameID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []ChatMessage
|
||||
for rows.Next() {
|
||||
var i ChatMessage
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.GameID,
|
||||
&i.Nickname,
|
||||
&i.Color,
|
||||
&i.Message,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
@@ -8,6 +8,15 @@ import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type ChatMessage struct {
|
||||
ID int64
|
||||
GameID string
|
||||
Nickname string
|
||||
Color int64
|
||||
Message string
|
||||
CreatedAt int64
|
||||
}
|
||||
|
||||
type Game struct {
|
||||
ID string
|
||||
Board string
|
||||
|
||||
15
db/migrations/006_add_chat_messages.sql
Normal file
15
db/migrations/006_add_chat_messages.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE chat_messages (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
game_id TEXT NOT NULL,
|
||||
nickname TEXT NOT NULL,
|
||||
color INTEGER NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX idx_chat_messages_game ON chat_messages(game_id, created_at);
|
||||
|
||||
-- +goose Down
|
||||
DROP INDEX IF EXISTS idx_chat_messages_game;
|
||||
DROP TABLE IF EXISTS chat_messages;
|
||||
@@ -3,10 +3,12 @@ package db
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"slices"
|
||||
|
||||
"github.com/ryanhamamura/c4/db/gen"
|
||||
"github.com/ryanhamamura/c4/game"
|
||||
"github.com/ryanhamamura/c4/snake"
|
||||
"github.com/ryanhamamura/c4/ui"
|
||||
)
|
||||
|
||||
type GamePersister struct {
|
||||
@@ -286,3 +288,40 @@ func (p *SnakePersister) DeleteSnakeGame(id string) error {
|
||||
ctx := context.Background()
|
||||
return p.queries.DeleteSnakeGame(ctx, id)
|
||||
}
|
||||
|
||||
type ChatPersister struct {
|
||||
queries *gen.Queries
|
||||
}
|
||||
|
||||
func NewChatPersister(q *gen.Queries) *ChatPersister {
|
||||
return &ChatPersister{queries: q}
|
||||
}
|
||||
|
||||
func (p *ChatPersister) SaveChatMessage(gameID string, msg ui.C4ChatMessage) error {
|
||||
return p.queries.CreateChatMessage(context.Background(), gen.CreateChatMessageParams{
|
||||
GameID: gameID,
|
||||
Nickname: msg.Nickname,
|
||||
Color: int64(msg.Color),
|
||||
Message: msg.Message,
|
||||
CreatedAt: msg.Time,
|
||||
})
|
||||
}
|
||||
|
||||
func (p *ChatPersister) LoadChatMessages(gameID string) ([]ui.C4ChatMessage, error) {
|
||||
rows, err := p.queries.GetChatMessages(context.Background(), gameID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgs := make([]ui.C4ChatMessage, len(rows))
|
||||
for i, r := range rows {
|
||||
msgs[i] = ui.C4ChatMessage{
|
||||
Nickname: r.Nickname,
|
||||
Color: int(r.Color),
|
||||
Message: r.Message,
|
||||
Time: r.CreatedAt,
|
||||
}
|
||||
}
|
||||
// Query returns newest-first; reverse to oldest-first for display
|
||||
slices.Reverse(msgs)
|
||||
return msgs, nil
|
||||
}
|
||||
|
||||
9
db/queries/chat.sql
Normal file
9
db/queries/chat.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
-- name: CreateChatMessage :exec
|
||||
INSERT INTO chat_messages (game_id, nickname, color, message, created_at)
|
||||
VALUES (?, ?, ?, ?, ?);
|
||||
|
||||
-- name: GetChatMessages :many
|
||||
SELECT * FROM chat_messages
|
||||
WHERE game_id = ?
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT 50;
|
||||
Reference in New Issue
Block a user