Replace hidden flag with post state.
CI / test (pull_request) Successful in 6m16s

Store post state as text so Go owns the allowed values and future states such as locked remain representable without a database enum migration.
This commit is contained in:
2026-08-27 00:37:17 -07:00
parent e918e5bd1d
commit b481dd2925
9 changed files with 372 additions and 104 deletions
+36 -22
View File
@@ -21,6 +21,14 @@ var (
ErrPostNotVotable = errors.New("post not votable")
)
type PostState string
const (
PostStateVisible PostState = "visible"
PostStateHidden PostState = "hidden"
PostStateLocked PostState = "locked"
)
// Post is either a root question (ParentID nil) or a reply to another post.
type Post struct {
ID string
@@ -32,7 +40,7 @@ type Post struct {
Body string
City string
PostDate string
Hidden bool
PostState PostState
CreatedAt string
UpdatedAt string
Score int
@@ -63,7 +71,7 @@ func (p *Post) Create(ctx context.Context) error {
Body: p.Body,
City: p.City,
PostDate: p.PostDate,
Hidden: boolInt32(p.Hidden),
PostState: string(p.PostState),
CreatedAt: p.CreatedAt,
UpdatedAt: p.UpdatedAt,
})
@@ -101,6 +109,14 @@ func preparePost(p *Post) error {
p.Body = strings.TrimSpace(p.Body)
p.City = strings.TrimSpace(p.City)
p.PostDate = strings.TrimSpace(p.PostDate)
if p.PostState == "" {
p.PostState = PostStateVisible
}
switch p.PostState {
case PostStateVisible, PostStateHidden, PostStateLocked:
default:
return fmt.Errorf("%w: invalid post state", ErrInvalidPost)
}
if p.AuthorID == "" {
return fmt.Errorf("%w: author is required", ErrInvalidPost)
}
@@ -120,7 +136,7 @@ func preparePost(p *Post) error {
return fmt.Errorf("%w: parent is required", ErrInvalidPost)
}
p.ParentID = &parentID
if p.Title != "" || p.City != "" || p.PostDate != "" || p.Hidden {
if p.Title != "" || p.City != "" || p.PostDate != "" || p.PostState != PostStateVisible {
return fmt.Errorf("%w: reply contains root-only fields", ErrInvalidPost)
}
}
@@ -152,13 +168,6 @@ func parentIDFromNull(parentID sql.NullString) *string {
return &id
}
func boolInt32(value bool) int32 {
if value {
return 1
}
return 0
}
func mapPostCreateError(err error) error {
if err == nil {
return nil
@@ -178,7 +187,7 @@ func postFromValues(
id string,
parentID sql.NullString,
authorID, authorName, authorRole, title, body, city, postDate string,
hidden int32,
postState string,
createdAt, updatedAt string,
) Post {
return Post{
@@ -191,7 +200,7 @@ func postFromValues(
Body: body,
City: city,
PostDate: postDate,
Hidden: hidden != 0,
PostState: PostState(postState),
CreatedAt: createdAt,
UpdatedAt: updatedAt,
db: db,
@@ -215,7 +224,7 @@ func GetPost(ctx context.Context, db *sql.DB, id string) (*Post, error) {
r.Body,
r.City,
r.PostDate,
r.Hidden,
r.PostState,
r.CreatedAt,
r.UpdatedAt,
)
@@ -241,7 +250,7 @@ func GetPostThread(ctx context.Context, db *sql.DB, rootID string) (*Post, error
r.Body,
r.City,
r.PostDate,
r.Hidden,
r.PostState,
r.CreatedAt,
r.UpdatedAt,
))
@@ -292,9 +301,10 @@ func buildPostTree(posts []Post, rootID string) (*Post, error) {
// ListRootPosts returns visible root posts for a post date.
func ListRootPosts(ctx context.Context, db *sql.DB, postDate, viewerID string) ([]Post, error) {
rows, err := sqlc.New(db).ListRootPosts(ctx, sqlc.ListRootPostsParams{
ViewerID: viewerID,
RowLimit: HuntListLimit,
PostDate: postDate,
ViewerID: viewerID,
RowLimit: HuntListLimit,
PostDate: postDate,
HiddenState: string(PostStateHidden),
})
if err != nil {
return nil, err
@@ -312,7 +322,7 @@ func ListRootPosts(ctx context.Context, db *sql.DB, postDate, viewerID string) (
r.Body,
r.City,
r.PostDate,
r.Hidden,
r.PostState,
r.CreatedAt,
r.UpdatedAt,
)
@@ -331,7 +341,10 @@ func SetPostVote(ctx context.Context, db *sql.DB, userID, postID string, value i
}
q := sqlc.New(db)
if value == 0 {
visible, err := q.PostIsVisibleRoot(ctx, postID)
visible, err := q.PostIsVisibleRoot(ctx, sqlc.PostIsVisibleRootParams{
ID: postID,
HiddenState: string(PostStateHidden),
})
if err != nil {
return err
}
@@ -344,9 +357,10 @@ func SetPostVote(ctx context.Context, db *sql.DB, userID, postID string, value i
})
}
n, err := q.UpsertPostVoteOnVisibleRoot(ctx, sqlc.UpsertPostVoteOnVisibleRootParams{
UserID: userID,
PostID: postID,
Value: int32(value),
UserID: userID,
PostID: postID,
Value: int32(value),
HiddenState: string(PostStateHidden),
})
if err != nil {
return err