Skip to content

Commit

Permalink
Various comment reply fixes (muting/reporting) (#10096)
Browse files Browse the repository at this point in the history
  • Loading branch information
DejayJD authored Oct 17, 2024
1 parent 98e7bd5 commit 761524e
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 79 deletions.
98 changes: 73 additions & 25 deletions packages/common/src/api/tan-query/comments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export const useGetCommentsByTrackId = ({
// TODO: why is this toString instead of encode
userId: userId?.toString() ?? undefined
})

const commentList = transformAndCleanList(
commentsRes.data,
commentFromSDK
Expand Down Expand Up @@ -142,31 +143,35 @@ export const useGetCommentById = (commentId: ID) => {

type GetRepliesArgs = {
commentId: ID
currentUserId?: Nullable<ID>
enabled?: boolean
pageSize?: number
}
export const useGetCommentRepliesById = ({
commentId,
enabled,
currentUserId,
pageSize = 3
}: GetRepliesArgs) => {
const { audiusSdk, reportToSentry } = useAudiusQueryContext()
const queryClient = useQueryClient()
const startingLimit = pageSize // comments will load in with 3 already so we don't start pagination at 0

const queryRes = useInfiniteQuery(
[QUERY_KEYS.comment, commentId, QUERY_KEYS.commentReplies],
{
enabled: !!enabled,
getNextPageParam: (lastPage: ReplyComment[], pages) => {
if (lastPage?.length < pageSize) return undefined
return (pages.length ?? 0) * pageSize
return (pages.length ?? pageSize) * pageSize + startingLimit
},
queryFn: async ({
pageParam: currentPage = 1
pageParam: currentPage = startingLimit
}): Promise<ReplyComment[]> => {
const sdk = await audiusSdk()
const commentsRes = await sdk.comments.getCommentReplies({
commentId: encodeHashId(commentId),
userId: currentUserId?.toString(),
limit: pageSize,
offset: currentPage
})
Expand Down Expand Up @@ -196,7 +201,8 @@ export const useGetCommentRepliesById = ({
})
toast({ content: messages.loadError('replies') })
},
staleTime: Infinity
staleTime: Infinity,
cacheTime: 1
}
)
return { ...queryRes, data: queryRes.data?.pages?.flat() ?? [] }
Expand Down Expand Up @@ -478,8 +484,9 @@ export const useDeleteComment = () => {
({
...prev,
replies: (prev?.replies ?? []).filter(
(reply) => reply.id !== commentId
)
(reply: ReplyComment) => reply.id !== commentId
),
replyCount: (prev?.replyCount ?? 0) - 1
} as Comment)
)
}
Expand All @@ -497,7 +504,7 @@ export const useDeleteComment = () => {
}
)
// Undo comment count change
dispatch(incrementTrackCommentCount(trackId, 1))
dispatch(incrementTrackCommentCount(trackId, -1))
},
onSuccess: (_res, { commentId }) => {
// We can safely wait till success to remove the individual comment from the cache because once its out of the sort or reply lists its not rendered anymore
Expand Down Expand Up @@ -606,6 +613,7 @@ export const useEditComment = () => {

type ReportCommentArgs = {
commentId: ID
parentCommentId?: ID
userId: ID
trackId: ID
currentSort: CommentSortMethod
Expand All @@ -619,20 +627,37 @@ export const useReportComment = () => {
const sdk = await audiusSdk()
await sdk.comments.reportComment(userId, commentId)
},
onMutate: ({ trackId, commentId, currentSort }) => {
// Optimistic update - filter out the comment
queryClient.setQueryData<InfiniteData<ID[]>>(
[QUERY_KEYS.trackCommentList, trackId, currentSort],
(prevData) => {
if (!prevData) return
const newState = cloneDeep(prevData)
// Filter out our reported comment
newState.pages = newState.pages.map((page) =>
page.filter((id) => id !== commentId)
)
return newState
}
)
onMutate: ({ trackId, commentId, currentSort, parentCommentId }) => {
// Optimistic update - filter out the comment from either the top list or the parent comment's replies
if (parentCommentId) {
queryClient.setQueryData<Comment>(
[QUERY_KEYS.comment, parentCommentId],
(prevData: Comment | undefined) => {
if (!prevData) return
return {
...prevData,
replies: prevData.replies?.filter(
(reply: ReplyComment) => reply.id !== commentId
),
replyCount: prevData.replyCount - 1
} as Comment
}
)
} else {
queryClient.setQueryData<InfiniteData<ID[]>>(
[QUERY_KEYS.trackCommentList, trackId, currentSort],
(prevData) => {
if (!prevData) return
const newState = cloneDeep(prevData)
// Filter out our reported comment
newState.pages = newState.pages.map((page) =>
page.filter((id) => id !== commentId)
)
return newState
}
)
}

queryClient.resetQueries([QUERY_KEYS.comment, commentId])
},
onError: (error: Error, args) => {
Expand Down Expand Up @@ -684,12 +709,35 @@ export const useMuteUser = () => {
// Filter out any comments by the muted user
newState.pages = newState.pages.map((page) =>
page.filter((id) => {
const comment = queryClient.getQueryData<
CommentOrReply | undefined
const rootComment = queryClient.getQueryData<
Comment | undefined
>([QUERY_KEYS.comment, id])
// If the comment is by the muted user, remove it
if (comment?.userId === mutedUserId) {
queryClient.resetQueries([QUERY_KEYS.comment, comment.id])
if (!rootComment) return false
// Check for any replies by our muted user first
if (
rootComment.replies &&
(rootComment.replies.length ?? 0) > 0
) {
// Keep track of count
const prevReplyCount = rootComment.replies.length
// Filter out replies by the muted user
rootComment.replies = rootComment.replies.filter((reply) => {
if (reply.userId === mutedUserId) {
queryClient.resetQueries([QUERY_KEYS.comment, reply.id])
return false
}
return true
})
// Subtract how many replies were removed from total reply count
// NOTE: remember that not all replies by the user may be showing due to pagination
rootComment.replyCount =
rootComment.replyCount -
(prevReplyCount - rootComment.replies.length)
}

// Finally if the root comment is by the muted user, remove it
if (rootComment?.userId === mutedUserId) {
queryClient.resetQueries([QUERY_KEYS.comment, rootComment.id])
return false
}
return true
Expand Down
10 changes: 3 additions & 7 deletions packages/common/src/context/comments/commentsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,7 @@ import {
import { useQueryClient } from '@tanstack/react-query'
import { useDispatch, useSelector } from 'react-redux'

import {
useGetCurrentUserId,
useGetTrackById,
useGetCommentsByTrackId,
QUERY_KEYS
} from '~/api'
import { useGetTrackById, useGetCommentsByTrackId, QUERY_KEYS } from '~/api'
import { useGatedContentAccess } from '~/hooks'
import {
ModalSource,
Expand All @@ -28,6 +23,7 @@ import {
ReplyComment,
UserTrackMetadata
} from '~/models'
import { getUserId } from '~/store/account/selectors'
import { tracksActions } from '~/store/pages/track/lineup/actions'
import { getLineup } from '~/store/pages/track/selectors'
import { seek } from '~/store/player/slice'
Expand Down Expand Up @@ -100,7 +96,7 @@ export function CommentSectionProvider<NavigationProp>(
setCurrentSort(sortMethod)
}

const { data: currentUserId } = useGetCurrentUserId({})
const currentUserId = useSelector(getUserId)
const {
data: commentIds = [],
status,
Expand Down
3 changes: 2 additions & 1 deletion packages/common/src/context/comments/commentsHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,11 @@ export const usePinComment = () => {
export const useReportComment = () => {
const { currentUserId, entityId, currentSort } = useCurrentCommentSection()
const { mutate: reportComment, ...rest } = useTqReportComment()
const wrappedHandler = (commentId: ID) => {
const wrappedHandler = (commentId: ID, parentCommentId?: ID) => {
if (currentUserId) {
reportComment({
commentId,
parentCommentId,
userId: currentUserId,
trackId: entityId,
currentSort
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from integration_tests.utils import populate_mock_db
from src.queries.get_comments import (
COMMENT_REPORT_KARMA_THRESHOLD,
get_comment_replies,
get_paginated_replies,
get_track_comments,
)
from src.utils.db_session import get_db
Expand Down Expand Up @@ -153,7 +153,7 @@ def test_get_comments_replies(app):
with app.app_context():
db = get_db()
populate_mock_db(db, test_entities)
comments = get_comment_replies(
comments = get_paginated_replies(
{"limit": 2, "offset": 2, "sort_method": "newest"}, 10
)
for comment in comments:
Expand Down
10 changes: 5 additions & 5 deletions packages/discovery-provider/src/api/v1/comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
from src.api.v1.helpers import (
decode_with_abort,
make_response,
pagination_parser,
pagination_with_current_user_parser,
success_response,
)
from src.api.v1.models.comments import reply_comment_model
from src.queries.get_comments import get_comment_replies
from src.queries.get_comments import get_paginated_replies
from src.queries.get_unclaimed_id import get_unclaimed_id
from src.utils.redis_cache import cache
from src.utils.redis_metrics import record_metrics
Expand All @@ -32,14 +32,14 @@ class CommentReplies(Resource):
params={"comment_id": "A Comment ID"},
responses={200: "Success", 400: "Bad request", 500: "Server error"},
)
@ns.expect(pagination_parser)
@ns.expect(pagination_with_current_user_parser)
@ns.marshal_with(comment_response)
@cache(ttl_sec=5)
def get(self, comment_id):
args = pagination_parser.parse_args()
args = pagination_with_current_user_parser.parse_args()
decoded_id = decode_with_abort(comment_id, ns)
current_user_id = args.get("user_id")
comment_replies = get_comment_replies(args, decoded_id, current_user_id)
comment_replies = get_paginated_replies(args, decoded_id, current_user_id)
return success_response(comment_replies)


Expand Down
48 changes: 36 additions & 12 deletions packages/discovery-provider/src/queries/get_comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def get_replies(
session,
parent_comment_id,
current_user_id,
# artist id could already have been queried for the track
# note: artist id already exists when used via get_track_comments - no need to requery for it
artist_id=None,
offset=0,
limit=COMMENT_REPLIES_LIMIT,
Expand All @@ -58,10 +58,34 @@ def get_replies(
)
.join(CommentThread, Comment.comment_id == CommentThread.comment_id)
.outerjoin(CommentReaction, Comment.comment_id == CommentReaction.comment_id)
.outerjoin(
MutedUser,
and_(
MutedUser.muted_user_id == Comment.user_id,
MutedUser.user_id == current_user_id,
),
)
.outerjoin(
CommentReport,
and_(
Comment.comment_id == CommentReport.comment_id,
CommentReport.user_id == current_user_id,
),
)
.group_by(Comment.comment_id)
.filter(
CommentThread.parent_comment_id == parent_comment_id,
Comment.is_delete == False,
or_(
MutedUser.muted_user_id == None,
MutedUser.is_delete == True,
), # Exclude muted users' comments
or_(
CommentReport.comment_id == None,
current_user_id == None,
CommentReport.user_id != current_user_id,
CommentReport.is_delete == True,
),
)
.order_by(asc(Comment.created_at))
.offset(offset)
Expand Down Expand Up @@ -96,20 +120,20 @@ def get_replies(
]


def get_reply_count(session, parent_comment_id):
reply_count = (
session.query(func.count(CommentThread.comment_id)).filter(
CommentThread.parent_comment_id == parent_comment_id,
)
).first()
return reply_count[0]


def get_comment_replies(args, comment_id, current_user_id=None):
# This method is only used by the API endpoint to get paginated replies directly /comments/<comment_id>/replies
# NOT used by the get_track_comments
def get_paginated_replies(args, comment_id, current_user_id=None):
offset, limit = format_offset(args), format_limit(args)
db = get_db_read_replica()
with db.scoped_session() as session:
replies = get_replies(session, comment_id, current_user_id, offset, limit)
replies = get_replies(
session,
comment_id,
current_user_id,
artist_id=None,
offset=offset,
limit=limit,
)

return replies

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,21 +187,21 @@ export const CommentOverflowMenu = (props: CommentOverflowMenuProps) => {
}, [currentSort, entityId, muteUser, toast, userId])

const handleFlagComment = useCallback(() => {
reportComment(id)
reportComment(id, parentCommentId)
toast({
content: messages.toasts.flaggedAndHidden,
type: 'info'
})
}, [reportComment, id, toast])
}, [reportComment, id, parentCommentId, toast])

const handleFlagAndRemoveComment = useCallback(() => {
reportComment(id)
reportComment(id, parentCommentId)
// TODO: remove comment
toast({
content: messages.toasts.flaggedAndRemoved,
type: 'info'
})
}, [reportComment, id, toast])
}, [reportComment, id, parentCommentId, toast])

const handlePinComment = useCallback(() => {
pinComment(id, !isPinned)
Expand Down
Loading

0 comments on commit 761524e

Please sign in to comment.