Add nested posts UI (#5)
## Summary - Cut hunt, question, submission, voting, hiding, and profile flows over to unified posts - Render nested replies with permission-aware inline Reply/Edit controls and edited markers - Add post profile queries and the author index migration they depend on Co-authored-by: codegirl-007 <s.raide@gmail.com>
This commit was merged in pull request #5.
This commit is contained in:
@@ -117,6 +117,34 @@ func (q *Queries) GetPost(ctx context.Context, id string) (GetPostRow, error) {
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getRootPostVoteSummary = `-- name: GetRootPostVoteSummary :one
|
||||
SELECT
|
||||
COALESCE(SUM(value), 0)::bigint AS score,
|
||||
COALESCE(
|
||||
MAX(value) FILTER (WHERE user_id = $1),
|
||||
0
|
||||
)::bigint AS user_vote
|
||||
FROM post_votes
|
||||
WHERE post_id = $2
|
||||
`
|
||||
|
||||
type GetRootPostVoteSummaryParams struct {
|
||||
ViewerID string
|
||||
RootID string
|
||||
}
|
||||
|
||||
type GetRootPostVoteSummaryRow struct {
|
||||
Score int64
|
||||
UserVote int64
|
||||
}
|
||||
|
||||
func (q *Queries) GetRootPostVoteSummary(ctx context.Context, arg GetRootPostVoteSummaryParams) (GetRootPostVoteSummaryRow, error) {
|
||||
row := q.db.QueryRowContext(ctx, getRootPostVoteSummary, arg.ViewerID, arg.RootID)
|
||||
var i GetRootPostVoteSummaryRow
|
||||
err := row.Scan(&i.Score, &i.UserVote)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listPostThread = `-- name: ListPostThread :many
|
||||
WITH RECURSIVE thread AS (
|
||||
SELECT p.id, p.parent_id, p.author_id, p.title, p.body, p.city, p.post_date, p.post_state, p.created_at, p.updated_at
|
||||
@@ -309,6 +337,162 @@ func (q *Queries) ListRootPosts(ctx context.Context, arg ListRootPostsParams) ([
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listRootPostsAnsweredBy = `-- name: ListRootPostsAnsweredBy :many
|
||||
WITH RECURSIVE ancestors AS (
|
||||
SELECT p.id, p.parent_id
|
||||
FROM posts p
|
||||
WHERE p.author_id = $3
|
||||
AND p.parent_id IS NOT NULL
|
||||
|
||||
UNION
|
||||
|
||||
SELECT parent.id, parent.parent_id
|
||||
FROM posts parent
|
||||
JOIN ancestors child ON child.parent_id = parent.id
|
||||
)
|
||||
SELECT DISTINCT
|
||||
root.id, root.parent_id, root.author_id,
|
||||
u.name AS author_name, u.role AS author_role,
|
||||
root.title, root.body, root.city, root.post_date,
|
||||
root.post_state, root.created_at, root.updated_at
|
||||
FROM posts root
|
||||
JOIN ancestors ON ancestors.id = root.id
|
||||
JOIN users u ON u.id = root.author_id
|
||||
WHERE root.parent_id IS NULL
|
||||
AND root.post_state <> $1
|
||||
ORDER BY root.created_at DESC, root.id DESC
|
||||
LIMIT $2
|
||||
`
|
||||
|
||||
type ListRootPostsAnsweredByParams struct {
|
||||
HiddenState string
|
||||
RowLimit int32
|
||||
AdminID string
|
||||
}
|
||||
|
||||
type ListRootPostsAnsweredByRow struct {
|
||||
ID string
|
||||
ParentID sql.NullString
|
||||
AuthorID string
|
||||
AuthorName string
|
||||
AuthorRole string
|
||||
Title string
|
||||
Body string
|
||||
City string
|
||||
PostDate string
|
||||
PostState string
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
}
|
||||
|
||||
func (q *Queries) ListRootPostsAnsweredBy(ctx context.Context, arg ListRootPostsAnsweredByParams) ([]ListRootPostsAnsweredByRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, listRootPostsAnsweredBy, arg.HiddenState, arg.RowLimit, arg.AdminID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListRootPostsAnsweredByRow{}
|
||||
for rows.Next() {
|
||||
var i ListRootPostsAnsweredByRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.ParentID,
|
||||
&i.AuthorID,
|
||||
&i.AuthorName,
|
||||
&i.AuthorRole,
|
||||
&i.Title,
|
||||
&i.Body,
|
||||
&i.City,
|
||||
&i.PostDate,
|
||||
&i.PostState,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); 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
|
||||
}
|
||||
|
||||
const listRootPostsByAuthor = `-- name: ListRootPostsByAuthor :many
|
||||
SELECT
|
||||
p.id, p.parent_id, p.author_id,
|
||||
u.name AS author_name, u.role AS author_role,
|
||||
p.title, p.body, p.city, p.post_date,
|
||||
p.post_state, p.created_at, p.updated_at
|
||||
FROM posts p
|
||||
JOIN users u ON u.id = p.author_id
|
||||
WHERE p.parent_id IS NULL
|
||||
AND p.author_id = $1
|
||||
AND p.post_state <> $2
|
||||
ORDER BY p.created_at DESC, p.id DESC
|
||||
LIMIT $3
|
||||
`
|
||||
|
||||
type ListRootPostsByAuthorParams struct {
|
||||
AuthorID string
|
||||
HiddenState string
|
||||
RowLimit int32
|
||||
}
|
||||
|
||||
type ListRootPostsByAuthorRow struct {
|
||||
ID string
|
||||
ParentID sql.NullString
|
||||
AuthorID string
|
||||
AuthorName string
|
||||
AuthorRole string
|
||||
Title string
|
||||
Body string
|
||||
City string
|
||||
PostDate string
|
||||
PostState string
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
}
|
||||
|
||||
func (q *Queries) ListRootPostsByAuthor(ctx context.Context, arg ListRootPostsByAuthorParams) ([]ListRootPostsByAuthorRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, listRootPostsByAuthor, arg.AuthorID, arg.HiddenState, arg.RowLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListRootPostsByAuthorRow{}
|
||||
for rows.Next() {
|
||||
var i ListRootPostsByAuthorRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.ParentID,
|
||||
&i.AuthorID,
|
||||
&i.AuthorName,
|
||||
&i.AuthorRole,
|
||||
&i.Title,
|
||||
&i.Body,
|
||||
&i.City,
|
||||
&i.PostDate,
|
||||
&i.PostState,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); 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
|
||||
}
|
||||
|
||||
const postIsVisibleRoot = `-- name: PostIsVisibleRoot :one
|
||||
SELECT EXISTS(
|
||||
SELECT 1
|
||||
@@ -353,6 +537,29 @@ func (q *Queries) UpdatePost(ctx context.Context, arg UpdatePostParams) (int64,
|
||||
return result.RowsAffected()
|
||||
}
|
||||
|
||||
const updateRootPostState = `-- name: UpdateRootPostState :execrows
|
||||
UPDATE posts
|
||||
SET
|
||||
post_state = $1,
|
||||
updated_at = $2
|
||||
WHERE id = $3
|
||||
AND parent_id IS NULL
|
||||
`
|
||||
|
||||
type UpdateRootPostStateParams struct {
|
||||
PostState string
|
||||
UpdatedAt string
|
||||
ID string
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateRootPostState(ctx context.Context, arg UpdateRootPostStateParams) (int64, error) {
|
||||
result, err := q.db.ExecContext(ctx, updateRootPostState, arg.PostState, arg.UpdatedAt, arg.ID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected()
|
||||
}
|
||||
|
||||
const upsertPostVoteOnVisibleRoot = `-- name: UpsertPostVoteOnVisibleRoot :execrows
|
||||
INSERT INTO post_votes (user_id, post_id, value)
|
||||
SELECT $1, $2, $3
|
||||
|
||||
Reference in New Issue
Block a user