|
| 1 | +package dbv1 |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "time" |
| 6 | + |
| 7 | + "bridgerton.audius.co/trashid" |
| 8 | + "github.com/jackc/pgx/v5" |
| 9 | + "github.com/jackc/pgx/v5/pgtype" |
| 10 | +) |
| 11 | + |
| 12 | +type GetCommentsParams struct { |
| 13 | + MyID interface{} `json:"my_id"` |
| 14 | + Ids []int32 `json:"ids"` |
| 15 | +} |
| 16 | + |
| 17 | +type FullComment struct { |
| 18 | + Id trashid.HashId `json:"id"` |
| 19 | + EntityType string `json:"entity_type"` |
| 20 | + EntityId trashid.HashId `json:"entity_id"` |
| 21 | + UserId trashid.HashId `json:"user_id"` |
| 22 | + Message string `json:"message"` |
| 23 | + Mentions []struct { |
| 24 | + UserId int `json:"user_id"` |
| 25 | + Handle string `json:"handle"` |
| 26 | + } `json:"mentions"` |
| 27 | + TrackTimestampS pgtype.Int4 `json:"track_timestamp_s"` |
| 28 | + IsMuted bool `json:"is_muted"` |
| 29 | + IsEdited bool `json:"is_edited"` |
| 30 | + IsCurrentUserReacted bool `json:"is_current_user_reacted"` |
| 31 | + IsArtistReacted bool `json:"is_artist_reacted"` |
| 32 | + IsDelete bool `json:"-"` |
| 33 | + IsTombstone bool `json:"is_tombstone"` |
| 34 | + ReactCount int `json:"react_count"` |
| 35 | + CreatedAt time.Time `json:"created_at"` |
| 36 | + UpdatedAt time.Time `json:"updated_at"` |
| 37 | + |
| 38 | + ReplyCount int `json:"reply_count"` |
| 39 | + Replies []FullComment `json:"replies"` |
| 40 | + |
| 41 | + // this should be omitted |
| 42 | + ReplyIds []int32 `db:"reply_ids" json:"-"` |
| 43 | + ParentCommentId pgtype.Int4 `json:"-"` |
| 44 | +} |
| 45 | + |
| 46 | +func (q *Queries) FullCommentsKeyed(ctx context.Context, arg GetCommentsParams) (map[int32]FullComment, error) { |
| 47 | + if len(arg.Ids) == 0 { |
| 48 | + return nil, nil |
| 49 | + } |
| 50 | + |
| 51 | + sql := ` |
| 52 | + SELECT |
| 53 | + comment_id as id, |
| 54 | + parent_comment_id, |
| 55 | + entity_type, |
| 56 | + entity_id, |
| 57 | + user_id, |
| 58 | + text as message, |
| 59 | +
|
| 60 | + ( |
| 61 | + SELECT json_agg( |
| 62 | + json_build_object( |
| 63 | + 'user_id', m.user_id, |
| 64 | + 'handle', handle |
| 65 | + ) |
| 66 | + ) |
| 67 | + FROM ( |
| 68 | + SELECT user_id, handle FROM comment_mentions |
| 69 | + JOIN users USING (user_id) |
| 70 | + WHERE comment_id = comments.comment_id |
| 71 | + ) m |
| 72 | + )::jsonb as mentions, |
| 73 | +
|
| 74 | + track_timestamp_s, |
| 75 | +
|
| 76 | + ( |
| 77 | + SELECT count(*) |
| 78 | + FROM comment_reactions |
| 79 | + WHERE comment_id = comments.comment_id |
| 80 | + AND is_delete = false |
| 81 | + ) as react_count, |
| 82 | +
|
| 83 | +
|
| 84 | + ( |
| 85 | + SELECT array_agg(comment_id) |
| 86 | + FROM comment_threads |
| 87 | + JOIN comments cc USING (comment_id) |
| 88 | + WHERE parent_comment_id = comments.comment_id |
| 89 | + AND cc.is_delete = false |
| 90 | + ) as reply_ids, |
| 91 | +
|
| 92 | + is_edited, |
| 93 | +
|
| 94 | + EXISTS ( |
| 95 | + SELECT 1 |
| 96 | + FROM comment_reactions |
| 97 | + WHERE comment_id = comments.comment_id |
| 98 | + AND user_id = @my_id |
| 99 | + AND is_delete = false |
| 100 | + ) AS is_current_user_reacted, |
| 101 | +
|
| 102 | + EXISTS ( |
| 103 | + SELECT 1 |
| 104 | + FROM comment_reactions |
| 105 | + WHERE comment_id = comments.comment_id |
| 106 | + AND user_id = tracks.owner_id |
| 107 | + AND is_delete = false |
| 108 | + ) AS is_artist_reacted, |
| 109 | +
|
| 110 | + comments.is_delete, |
| 111 | +
|
| 112 | + coalesce(( |
| 113 | + SELECT is_muted |
| 114 | + FROM comment_notification_settings mutes |
| 115 | + WHERE @my_id > 0 |
| 116 | + AND mutes.user_id = @my_id |
| 117 | + AND mutes.entity_type = entity_type |
| 118 | + AND mutes.entity_id = entity_id |
| 119 | + LIMIT 1 |
| 120 | + ), false) as is_muted, |
| 121 | +
|
| 122 | + comments.created_at, |
| 123 | + comments.updated_at |
| 124 | +
|
| 125 | + FROM comments |
| 126 | + JOIN tracks ON entity_id = track_id |
| 127 | + LEFT JOIN comment_threads USING (comment_id) |
| 128 | + WHERE comment_id = ANY(@ids::int[]) |
| 129 | + ORDER BY comments.created_at DESC |
| 130 | + ` |
| 131 | + |
| 132 | + rows, err := q.db.Query(ctx, sql, pgx.NamedArgs{ |
| 133 | + "ids": arg.Ids, |
| 134 | + "my_id": arg.MyID, |
| 135 | + }) |
| 136 | + if err != nil { |
| 137 | + return nil, err |
| 138 | + } |
| 139 | + |
| 140 | + comments, err := pgx.CollectRows(rows, pgx.RowToStructByNameLax[FullComment]) |
| 141 | + if err != nil { |
| 142 | + return nil, err |
| 143 | + } |
| 144 | + |
| 145 | + commentMap := map[int32]FullComment{} |
| 146 | + for _, comment := range comments { |
| 147 | + commentMap[int32(comment.Id)] = comment |
| 148 | + } |
| 149 | + |
| 150 | + // fetch replies |
| 151 | + replyIds := []int32{} |
| 152 | + for _, comment := range comments { |
| 153 | + replyIds = append(replyIds, comment.ReplyIds...) |
| 154 | + } |
| 155 | + replyMap, err := q.FullCommentsKeyed(ctx, GetCommentsParams{ |
| 156 | + MyID: arg.MyID, |
| 157 | + Ids: replyIds, |
| 158 | + }) |
| 159 | + if err != nil { |
| 160 | + return nil, err |
| 161 | + } |
| 162 | + |
| 163 | + for id, comment := range commentMap { |
| 164 | + for _, replyId := range comment.ReplyIds { |
| 165 | + if reply, ok := replyMap[replyId]; ok { |
| 166 | + comment.Replies = append(comment.Replies, reply) |
| 167 | + } |
| 168 | + } |
| 169 | + // todo: sort replies? |
| 170 | + comment.ReplyCount = len(comment.Replies) |
| 171 | + |
| 172 | + if comment.IsDelete { |
| 173 | + comment.Message = "[Removed]" |
| 174 | + if comment.ReplyCount > 0 { |
| 175 | + comment.IsTombstone = true |
| 176 | + } |
| 177 | + } |
| 178 | + commentMap[id] = comment |
| 179 | + } |
| 180 | + |
| 181 | + return commentMap, nil |
| 182 | + |
| 183 | +} |
| 184 | + |
| 185 | +func (q *Queries) FullComments(ctx context.Context, arg GetCommentsParams) ([]FullComment, error) { |
| 186 | + commentMap, err := q.FullCommentsKeyed(ctx, arg) |
| 187 | + if err != nil { |
| 188 | + return nil, err |
| 189 | + } |
| 190 | + |
| 191 | + comments := make([]FullComment, 0, len(arg.Ids)) |
| 192 | + for _, id := range arg.Ids { |
| 193 | + if c, ok := commentMap[id]; ok { |
| 194 | + comments = append(comments, c) |
| 195 | + } |
| 196 | + } |
| 197 | + return comments, nil |
| 198 | +} |
0 commit comments