// 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"
)
)
// Adding a comment with attachments
let commentWithAttachment = try await feed.addComment(
request: .init(
comment: "Check out this image!",
objectId: "activity_123",
objectType: "activity",
attachments: [
Attachment(
imageUrl: "https://example.com/image.jpg",
type: "image"
)
]
)
)Comments
Comments support voting, ranking, threading, images, URL previews, mentions and notifications.
Adding Comments
// Get feed instance
feed := client.Feeds().Feed("user", "john")
feedResponse, err := feed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
UserID: getstream.PtrTo("john"),
})
activity := feedResponse.Data.Activities[0]
activityID := activity.ID
commentID := "6b6dd9df-13b0-4b47-8d73-a45a00d04fa7"
// folderID := "11e7d97a-e461-47f0-8d24-cd8a86168a60"
// Adding a comment to an activity
_, err = client.Feeds().AddComment(context.Background(), &getstream.AddCommentRequest{
ID: getstream.PtrTo("comment_123"), // ID is optional, but must be unique. will be auto-generated if not provided
Comment: "So great!",
ObjectID: activityID,
ObjectType: "activity",
UserID: getstream.PtrTo("john"),
Custom: map[string]any{
"sentiment": "positive",
},
})
if err != nil {
log.Fatal("Error adding comment:", err)
}
// Adding a reply to a comment
_, err = client.Feeds().AddComment(context.Background(), &getstream.AddCommentRequest{
Comment: "I agree!",
ParentID: getstream.PtrTo(commentID),
UserID: getstream.PtrTo("john"),
})
if err != nil {
log.Fatal("Error adding reply:", err)
}
// Adding a comment without triggering push notifications
_, err = client.Feeds().AddComment(context.Background(), &getstream.AddCommentRequest{
Comment: "Silent comment",
ObjectID: activityID,
ObjectType: "activity",
UserID: getstream.PtrTo("john"),
SkipPush: getstream.PtrTo(true),
})
if err != nil {
log.Fatal("Error adding silent comment:", err)
}
// Adding a comment with attachments
_, err = client.Feeds().AddComment(context.Background(), &getstream.AddCommentRequest{
Comment: "Check out this image!",
ObjectID: activityID,
ObjectType: "activity",
UserID: getstream.PtrTo("john"),
Attachments: []getstream.Attachment{
{
AssetUrl: getstream.PtrTo("https://example.com/image.png"),
Type: getstream.PtrTo("image"),
Title: getstream.PtrTo("Image"),
Custom: map[string]any{},
},
},
})
if err != nil {
log.Fatal("Error adding comment with attachment:", err)
}You can use Stream's CDN for storing and quickly accessing files attached to comments.
Comment request options
Updating Comments
updateComment endpoint performs a partial update: only the fields you include in the request are changed, and each of those fields is completely overwritten.
updatedComment, err := client.Feeds().UpdateComment(context.Background(), commentID, &getstream.UpdateCommentRequest{
Comment: getstream.PtrTo("Updated comment"),
})
if err != nil {
log.Fatal("Error updating comment:", err)
}
log.Println("Comment edited at:", updatedComment.EditedAt)Removing Comments
_, err = client.Feeds().DeleteComment(context.Background(), commentID, &getstream.DeleteCommentRequest{})
if err != nil {
log.Fatal("Error deleting comment:", err)
}Reading Comments
You'll also want to show/return these comments. The most important is when reading the feed.
feed := client.Feeds().Feed("user", "john")
feedResponse, err := feed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
UserID: getstream.PtrTo("john"),
})
if err != nil {
log.Fatal(err)
}
// Access comments from the first activity
activity := feedResponse.Data.Activities[0]
comments := activity.Comments
// Or using the getComments endpoint
commentsResponse, err := client.Feeds().GetComments(context.Background(), &getstream.GetCommentsRequest{
ObjectType: "activity",
ObjectID: "123",
Limit: getstream.PtrTo(10),
Sort: getstream.PtrTo("best"),
})
if err != nil {
log.Fatal(err)
}
commentsFromEndpoint := commentsResponse.Data.Comments
// Reading a single comment
comment, err := client.Feeds().GetComment(context.Background(), "123", &getstream.GetCommentRequest{})
if err != nil {
log.Fatal(err)
}Sort options
The following sort options are supported when reading comments:
last: newest comment returned firstfirst: oldest comment returned firsttop: highest score returned first - computed fromupvote-downvotereaction typescontroversial: controversial comment returned first - mixed reactions (upvoteanddownvote) and lots of commentsbest: 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
_, err = client.Feeds().QueryComments(context.Background(), &getstream.QueryCommentsRequest{
Filter: map[string]any{
"comment_text": map[string]any{
"$q": "oat",
},
},
})
if err != nil {
log.Printf("Error searching comments: %v", err)
}
// Comments by user
_, err = client.Feeds().QueryComments(context.Background(), &getstream.QueryCommentsRequest{
Filter: map[string]any{
"user_id": "jane",
},
Limit: getstream.PtrTo(20),
})
if err != nil {
log.Printf("Error querying user comments: %v", err)
}Comment Queryable Built-In Fields
| name | type | description | supported operations | example |
|---|---|---|---|---|
id | string or list of strings | The ID of the comment | $in, $eq | { id: { $in: [ 'comment_123', 'comment_456' ] } } |
user_id | string or list of strings | The ID of the user who created the comment | $in, $eq | { user_id: { $eq: 'user_123' } } |
object_type | string or list of strings | The type of object being commented on | $eq, $ne, $in, $nin | { object_type: { $in: [ 'activity', 'post' ] } } |
object_id | string or list of strings | The ID of the object being commented on | $in, $eq | { object_id: { $eq: 'activity_123' } } |
parent_id | string or list of strings | The parent comment ID for replies | $in, $eq | { parent_id: { $eq: 'comment_parent_123' } } |
comment_text | string | The text content of the comment | $q | { comment_text: { $q: 'search terms' } } |
status | string or list of strings | The status of the comment | $eq, $ne, $in | { status: { $in: [ 'approved', 'pending' ] } } |
reply_count | number | The number of replies to this comment | $gt, $gte, $lt, $lte | { reply_count: { $gte: 5 } } |
upvote_count | number | The number of upvotes on the comment | $gt, $gte, $lt, $lte | { upvote_count: { $gte: 10 } } |
downvote_count | number | The number of downvotes on the comment | $gt, $gte, $lt, $lte | { downvote_count: { $lt: 5 } } |
score | number | The overall score of the comment | $gt, $gte, $lt, $lte | { score: { $gte: 0 } } |
confidence_score | number | The confidence score of the comment | $gt, $gte, $lt, $lte | { confidence_score: { $gte: 0.5 } } |
controversy_score | number | The controversy score of the comment | $gt, $gte, $lt, $lte | { controversy_score: { $lt: 0.8 } } |
created_at | string, must be formatted as an RFC3339 timestamp | The 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.
created_atscore: same astopsort optionconfidence_score- same asbestsort optioncontroversy_score- same ascontroversialsort optionupvote_countdownvote_count
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 comment reaction
_, err = client.Feeds().AddCommentReaction(context.Background(), commentID, &getstream.AddCommentReactionRequest{
Type: "like",
UserID: getstream.PtrTo("john"),
})
if err != nil {
log.Fatal("Error adding comment reaction:", err)
}
// Delete comment reaction
_, err = client.Feeds().DeleteCommentReaction(context.Background(), commentID, "like", &getstream.DeleteCommentReactionRequest{
UserID: getstream.PtrTo("john"),
})
if err != nil {
log.Fatal("Error deleting comment reaction:", err)
}Overview of the reaction model
Comment Threading
// Get comments for an activity
_, err = client.Feeds().GetComments(context.Background(), &getstream.GetCommentsRequest{
ObjectID: "activity_123",
ObjectType: "activity",
// Depth of the threaded comments
Depth: getstream.PtrTo(3),
Limit: getstream.PtrTo(20),
})
if err != nil {
log.Fatal("Error getting comments:", err)
}
// Get replies of a specific parent comment
_, err = client.Feeds().GetCommentReplies(context.Background(), parentID, &getstream.GetCommentRepliesRequest{})
if err != nil {
log.Fatal("Error getting comment replies:", err)
}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.
feedsClient := client.Feeds()
response, err := feedsClient.AddActivity(context.Background(), &getstream.AddActivityRequest{
Type: "post",
Feeds: []string{"user:john"},
Text: getstream.PtrTo("apple stock will go up"),
UserID: getstream.PtrTo("john"),
RestrictReplies: getstream.PtrTo("people_i_follow"), // Options: "everyone", "people_i_follow", "nobody"
})
if err != nil {
log.Fatal("Error adding activity:", err)
}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_followingsis set by default - When reading a feed, you have to explicitly enable fetching
own_followings:
feed.getOrCreate({ enrichment_options: { enrich_own_followings: true } });Overview of the comment model
Add comments in batch
_, err = client.Feeds().AddCommentsBatch(context.Background(), &getstream.AddCommentsBatchRequest{
Comments: []getstream.AddCommentRequest{
{
ID: getstream.PtrTo("comment_123"), // ID is optional, but must be unique. will be auto-generated if not provided
Comment: "So great!",
ObjectID: "activity_123",
ObjectType: "activity",
UserID: getstream.PtrTo("john"),
Custom: map[string]any{
"sentiment": "positive",
},
},
},
})
if err != nil {
log.Fatal("Error adding comments batch:", err)
}