Switch avatar resize to ApproxBiLinear, cap hunt/profile/admin list queries, drop redundant admin/profile lookups, dedupe CI on app PRs, and refresh stale todo.md notes.
157 lines
4.1 KiB
Go
157 lines
4.1 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"plumber/internal/pacific"
|
|
"plumber/internal/store/sqlc"
|
|
)
|
|
|
|
// RankedQuestion is a question row with score / vote annotations for lists.
|
|
type RankedQuestion struct {
|
|
ID string
|
|
AuthorID string
|
|
AuthorName string
|
|
Title string
|
|
Body string
|
|
City string
|
|
HuntDate string
|
|
Hidden bool
|
|
CreatedAt string
|
|
Score int
|
|
Answered bool
|
|
UserVote int
|
|
db *sql.DB
|
|
}
|
|
|
|
// NewQuestion returns a question bound to db (not yet inserted).
|
|
func NewQuestion(db *sql.DB) *RankedQuestion {
|
|
return &RankedQuestion{db: db}
|
|
}
|
|
|
|
// Create inserts the question. Sets ID, HuntDate, and CreatedAt when empty.
|
|
func (q *RankedQuestion) Create(ctx context.Context) error {
|
|
if q == nil || q.db == nil {
|
|
return fmt.Errorf("question: no database")
|
|
}
|
|
q.Title = strings.TrimSpace(q.Title)
|
|
q.Body = strings.TrimSpace(q.Body)
|
|
q.City = strings.TrimSpace(q.City)
|
|
if q.ID == "" {
|
|
q.ID = uuid.NewString()
|
|
}
|
|
if q.HuntDate == "" {
|
|
q.HuntDate = pacific.Today()
|
|
}
|
|
if q.CreatedAt == "" {
|
|
q.CreatedAt = time.Now().UTC().Format(time.RFC3339)
|
|
}
|
|
return sqlc.New(q.db).CreateQuestion(ctx, sqlc.CreateQuestionParams{
|
|
ID: q.ID,
|
|
AuthorID: q.AuthorID,
|
|
Title: q.Title,
|
|
Body: q.Body,
|
|
City: q.City,
|
|
HuntDate: q.HuntDate,
|
|
CreatedAt: q.CreatedAt,
|
|
})
|
|
}
|
|
|
|
// Hide marks the question hidden.
|
|
func (q *RankedQuestion) Hide(ctx context.Context) error {
|
|
if q == nil || q.db == nil {
|
|
return fmt.Errorf("question: no database")
|
|
}
|
|
if err := sqlc.New(q.db).HideQuestion(ctx, q.ID); err != nil {
|
|
return err
|
|
}
|
|
q.Hidden = true
|
|
return nil
|
|
}
|
|
|
|
func rankedFrom(
|
|
db *sql.DB,
|
|
id, authorID, authorName, title, body, city, huntDate, createdAt string,
|
|
hidden int32, score, answered, userVote int64,
|
|
) RankedQuestion {
|
|
return RankedQuestion{
|
|
ID: id,
|
|
AuthorID: authorID,
|
|
AuthorName: authorName,
|
|
Title: title,
|
|
Body: body,
|
|
City: city,
|
|
HuntDate: huntDate,
|
|
Hidden: hidden != 0,
|
|
CreatedAt: createdAt,
|
|
Score: int(score),
|
|
Answered: answered != 0,
|
|
UserVote: int(userVote),
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
func ListHunt(ctx context.Context, db *sql.DB, huntDate, viewerID string) ([]RankedQuestion, error) {
|
|
rows, err := sqlc.New(db).ListHunt(ctx, sqlc.ListHuntParams{
|
|
ViewerID: viewerID,
|
|
HuntDate: huntDate,
|
|
RowLimit: HuntListLimit,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]RankedQuestion, 0, len(rows))
|
|
for _, r := range rows {
|
|
out = append(out, rankedFrom(db, r.ID, r.AuthorID, r.AuthorName, r.Title, r.Body, r.City, r.HuntDate, r.CreatedAt, r.Hidden, r.Score, r.Answered, r.UserVote))
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func GetQuestion(ctx context.Context, db *sql.DB, id, viewerID string) (*RankedQuestion, error) {
|
|
r, err := sqlc.New(db).GetQuestion(ctx, sqlc.GetQuestionParams{
|
|
ViewerID: viewerID,
|
|
ID: id,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
q := rankedFrom(db, r.ID, r.AuthorID, r.AuthorName, r.Title, r.Body, r.City, r.HuntDate, r.CreatedAt, r.Hidden, r.Score, r.Answered, r.UserVote)
|
|
return &q, nil
|
|
}
|
|
|
|
func ListQuestionsByAuthor(ctx context.Context, db *sql.DB, authorID string) ([]RankedQuestion, error) {
|
|
rows, err := sqlc.New(db).ListQuestionsByAuthor(ctx, sqlc.ListQuestionsByAuthorParams{
|
|
AuthorID: authorID,
|
|
RowLimit: ProfileListLimit,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]RankedQuestion, 0, len(rows))
|
|
for _, r := range rows {
|
|
out = append(out, rankedFrom(db, r.ID, r.AuthorID, r.AuthorName, r.Title, r.Body, r.City, r.HuntDate, r.CreatedAt, r.Hidden, r.Score, r.Answered, r.UserVote))
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func ListQuestionsAnsweredBy(ctx context.Context, db *sql.DB, adminID string) ([]RankedQuestion, error) {
|
|
rows, err := sqlc.New(db).ListQuestionsAnsweredBy(ctx, sqlc.ListQuestionsAnsweredByParams{
|
|
AdminID: adminID,
|
|
RowLimit: ProfileListLimit,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]RankedQuestion, 0, len(rows))
|
|
for _, r := range rows {
|
|
out = append(out, rankedFrom(db, r.ID, r.AuthorID, r.AuthorName, r.Title, r.Body, r.City, r.HuntDate, r.CreatedAt, r.Hidden, r.Score, r.Answered, r.UserVote))
|
|
}
|
|
return out, nil
|
|
}
|