Comments

Overview

Comments support voting, ranking, threading, images, URL previews, mentions and notifications.

Adding Comments

// Adding a comment to an activity
let comment = try await feed.addComment(
    request: .init(
        id: "comment_123", // id is optional, but must be unique. will be auto-generated if not provided
        comment: "So great!",
        custom: ["sentiment": "positive"],
        objectId: "activity_123",
        objectType: "activity",
    )
)

// Adding a reply to a comment
let reply = try await feed.addComment(
    request: .init(
        comment: "I agree!",
        parentId: "comment_456"
    )
)

Overview of the comment model

CommentResponse

NameTypeDescriptionConstraints
attachmentsAttachment[]--
confidence_scorenumberConfidence score of the commentRequired
controversy_scorenumberControversy score of the comment-
created_atnumberWhen the comment was createdRequired
customobjectCustom data for the comment-
deleted_atnumberWhen the comment was deleted-
downvote_countintegerNumber of downvotes for this commentRequired
idstringUnique identifier for the commentRequired
latest_reactionsFeedsReactionResponse[]Recent reactions to the comment-
mentioned_usersUserResponse[]Users mentioned in the commentRequired
moderationModerationV2Response--
object_idstringID of the object this comment is associated withRequired
object_typestringType of the object this comment is associated withRequired
own_reactionsFeedsReactionResponse[]Current user's reactions to this activityRequired
parent_idstringID of parent comment for nested replies-
reaction_countintegerNumber of reactions to this commentRequired
reaction_groupsobjectGrouped reactions by type-
reply_countintegerNumber of replies to this commentRequired
scoreintegerScore of the comment based on reactionsRequired
statusstringStatus of the comment (e.g., active, deleted)Required
textstringText content of the comment-
updated_atnumberWhen the comment was last updatedRequired
upvote_countintegerNumber of upvotes for this commentRequired
userUserResponseUser who created the commentRequired

Updating Comments

try await feed.updateComment(
    commentId: "comment_123",
    request: .init(
        comment: "Not so great",
        custom: ["edited": true]
    )
)

Removing Comments

try await feed.deleteComment(
    commentId: "comment_123"
)

Reading Comments

You’ll also want to show/return these comments. The most important is when reading the feed.

try await feed.getOrCreate()
print(feed.state.activities[0].comments)
// or
let activity = client.activity(
    for: "activity_123",
    in: FeedId(group: "user", id: "john")
)
try await activity.get()
print(activity.state.comments)

Sort options

The following sort options are supported when reading comments:

  • last: newest comment returned first
  • first: oldest comment returned first
  • top: highest score returned first - computed from upvote - downvote reaction types
  • controversial: controversial comment returned first - mixed reactions (upvote and downvote) and lots of comments
  • best: highest Wilson score returned first - Wilson score balances reaction score (upvote - downvote) with number of reactions (a comment with lower reaction score but more sum reactions is returned before a comment with higher score, but few sum reactions)

Search for Comments

You can also query the comments, the following examples show

  • how to search across all comments by text
  • query all comments of a given user
// Search in comment texts
let list1 = client.commentList(
    for: .init(
        filter: .query(.commentText, "oat")
    )
)
let comments1 = try await list1.get()

// Comments from an user
let list2 = client.commentList(
    for: .init(
        filter: .equal(.userId, "jane")
    )
)
let comments2 = try await list2.get()

Comment Queryable Built-In Fields

nametypedescriptionsupported operationsexample
idstring or list of stringsThe ID of the comment$in, $eq{ id: { $in: [ 'comment_123', 'comment_456' ] } }
user_idstring or list of stringsThe ID of the user who created the comment$in, $eq{ user_id: { $eq: 'user_123' } }
object_typestring or list of stringsThe type of object being commented on$eq, $ne, $in, $nin{ object_type: { $in: [ 'activity', 'post' ] } }
object_idstring or list of stringsThe ID of the object being commented on$in, $eq{ object_id: { $eq: 'activity_123' } }
parent_idstring or list of stringsThe parent comment ID for replies$in, $eq{ parent_id: { $eq: 'comment_parent_123' } }
comment_textstringThe text content of the comment$q{ comment_text: { $q: 'search terms' } }
statusstring or list of stringsThe status of the comment$eq, $ne, $in{ status: { $in: [ 'approved', 'pending' ] } }
reply_countnumberThe number of replies to this comment$gt, $gte, $lt, $lte{ reply_count: { $gte: 5 } }
upvote_countnumberThe number of upvotes on the comment$gt, $gte, $lt, $lte{ upvote_count: { $gte: 10 } }
downvote_countnumberThe number of downvotes on the comment$gt, $gte, $lt, $lte{ downvote_count: { $lt: 5 } }
scorenumberThe overall score of the comment$gt, $gte, $lt, $lte{ score: { $gte: 0 } }
confidence_scorenumberThe confidence score of the comment$gt, $gte, $lt, $lte{ confidence_score: { $gte: 0.5 } }
controversy_scorenumberThe controversy score of the comment$gt, $gte, $lt, $lte{ controversy_score: { $lt: 0.8 } }
created_atstring, must be formatted as an RFC3339 timestampThe time the comment was created$eq, $gt, $gte, $lt, $lte{ created_at: { $gte: '2023-12-04T09:30:20.45Z' } }

Comment sort options

All fields support 1 and -1 directions.

Comment Reactions

When adding reactions and the enforce_unique flag is set to true, the existing reaction of a user will be overridden with the new reaction. Use this flag if you want to ensure users have a single reaction per each comment/activity. The default value is false, in which case users can have multiple reactions.

// Add a reaction to a comment
try await feed.addCommentReaction(
    commentId: "comment_123",
    request: .init(type: "like")
)

// Remove a reaction from a comment
try await feed.deleteCommentReaction(
    commentId: "comment_123",
    type: "like"
)

Overview of the reaction model

FeedsReactionResponse
NameTypeDescriptionConstraints
activity_idstringID of the activity that was reacted toRequired
comment_idstringID of the comment that was reacted to-
created_atnumberWhen the reaction was createdRequired
customobjectCustom data for the reaction-
typestringType of reactionRequired
updated_atnumberWhen the reaction was last updatedRequired
userUserResponseUser who created the reactionRequired

Comment Threading

let commentList = client.activityCommentList(
    for: .init(
        objectId: "activity_123",
        objectType: "activity",
        depth: 3,
        limit: 20
    )
)
let comments = try await commentList.get()

// Get replies of a specific parent comment
let replyList = client.commentReplyList(
    for: .init(
        commentId: "parent_123"
    )
)
let replies = try await replyList.get()

Reading comment replies supports the same sort parameters as reading comments does.

Restricting comment replies

Set who can add comments

When creating an activity it’s possible to set who can add comments. Possible options:

  • Everyone (this is the default setting)
  • Only people I follow
  • Nobody

The activity author is always allowed to add comments regardless of the reply settings.

const response = await feed.addActivity({
  type: "post",
  text: "apple stock will go up",
  restrict_replies: "people_i_follow", // Options: "everyone", "people_i_follow", "nobody"
});

Show/hide comment input

If a user isn’t allowed to add comments, it’s a good idea to hide the comment input.

When everyone/nobody can comment this is straightforward. However if only people the activity author follows can comment, we have to check if there are any feeds owned by the current user that is followed by the activity author, the code snippet below shows how to check this:

const activity: ActivityResponse = /* fetch a single activity or read a feed */;

// Lists follow relationships where
// - target feed is owned by current user
// - source feed is owned by activity author
const feed = client.feed(
  activity.current_feed.group_id,
  activity.current_feed.id,
);
console.log(feed.currentState.own_followings);

own_followings is only set when reading a feed/activity from client-side, for server-side requests it will be empty.

  • When fetching a single activity, own_followings is set by default
  • When reading a feed, you have to explicitly enable fetching own_followings:
feed.getOrCreate({ enrichment_options: { enrich_own_followings: true } });