Threads & Replies

LAST EDIT Apr 08 2021

Threads and replies provide your users with a way to go into more detail about a specific topic.

This can be very helpful to keep the conversation organized and reduce noise. To create a thread you simply send a message with a parent_id. Have a look at the example below:

1
2
3
4
5
const reply = await channel.sendMessage({ 
    text: 'Hey, I am replying to a message!', 
    parent_id: parentID, 
    show_in_channel: false, 
});
1
2
3
4
5
6
7
8
9
10
11
12
Message message = new Message(); 
message.setText("Hello there!"); 
message.setParentId(parentMessage.getId()); 
 
// Send the message to the channel 
channelClient.sendMessage(message).enqueue(result -> { 
    if (result.isSuccess()) { 
        Message sentMessage = result.data(); 
    } else { 
        // Handle result.error() 
    } 
});
1
2
3
4
5
6
7
8
9
10
11
12
13
import StreamChat 
 
/// 1: Create a `ChannelId` that represents the channel you want to get a message from. 
let channelId = ChannelId(type: .messaging, id: "general") 
 
/// 2: Create a `MessageId` that represents the message you want to get. 
let messageId = "message-id" 
 
/// 3: Use the `ChatClient` to create a `ChatMessageController` with the `ChannelId` and message id. 
let messageController = chatClient.messageController(cid: channelId, messageId: messageId) 
 
/// 4: Call `ChatMessageController.createNewReply` to start a thread. 
messageController.createNewReply(text: "What's up?")
1
2
3
4
5
6
7
8
9
10
11
12
13
val message = Message( 
    text = "Hello there!", 
    parentId = parentMessage.id, 
) 
 
// Send the message to the channel 
channelClient.sendMessage(message).enqueue { result -> 
    if (result.isSuccess) { 
        val sentMessage = result.data() 
    } else { 
        // Handle result.error() 
    } 
}
1
2
3
4
5
6
7
$response = $channel->sendMessage([ 
		'text' => 'Hey, I am replying to a message!', 
		'parent_id' => 'parent-id', 
		'show_in_channel' => false 
		],  
		'jenny' 
	);
1
2
3
4
5
final reply = await channel.sendMessage( 
    Message(text: 'Hey, I am replying to a message!', 
    parentId: parentID, 
    showInChannel: false, 
));
If you specify show_in_channel, the message will be visible both in a thread of replies as well as the main channel.

Messages inside a thread can also have reactions, attachments and mention as any other message.

Thread Pagination

Copied!

When you read a channel you do not receive messages inside threads. The parent message includes the count of replies which it is usually what apps show as the link to the thread screen. Reading a thread and paginating its messages works in a very similar way as paginating a channel.

1
2
3
4
5
// retrieve the first 20 messages inside the thread 
await channel.getReplies(parentMessageId, {limit: 20}); 
 
// retrieve the 20 more messages before the message with id "42" 
await channel.getReplies(parentMessageId, {limit: 20, id_lte: "42"});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Retrieve the first 20 messages inside the thread 
client.getReplies(parentMessage.id, limit = 20).enqueue { result -> 
    if (result.isSuccess) { 
        val replies: List<Message> = result.data() 
    } else { 
        // Handle result.error() 
    } 
} 
 
// Retrieve the 20 more messages before the message with id "42" 
client.getRepliesMore( 
    messageId = parentMessage.id, 
    firstId = "42", 
    limit = 20, 
).enqueue { /* ... */ }
1
2
3
4
5
6
7
8
let message = Message(text: "Hello!") 
// retrieve the first 20 messages inside the thread 
message.replies(pagination: [.limit(20)]) { (result) in 
// handle result 
} 
     
// retrieve the 20 more messages before the message with id "42" 
message.replies(pagination: [.lessThanOrEqual("42"), .limit(20)]) { (result) in /**/ }
1
2
3
4
5
6
7
8
9
10
11
12
int limit = 20; 
// Retrieve the first 20 messages inside the thread 
client.getReplies(parentMessage.getId(), limit).enqueue(result -> { 
    if (result.isSuccess()) { 
        List<Message> replies = result.data(); 
    } else { 
        // Handle result.error() 
    } 
}); 
 
// Retrieve the 20 more messages before the message with id "42" 
client.getRepliesMore(parentMessage.getId(), "42", limit).enqueue(result -> { /* ... */ });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import StreamChat 
 
/// 1: Create a `ChannelId` that represents the channel you want to get a message from. 
let channelId = ChannelId(type: .messaging, id: "general") 
 
/// 2: Create a `MessageId` that represents the message you want to get. 
let messageId = "message-id" 
 
/// 3: Use the `ChatClient` to create a `ChatMessageController` with the `ChannelId` and message id. 
let controller = chatClient.messageController(cid: channelId, messageId: messageId) 
 
/// 4: Call `ChatMessageController.loadNextReplies` to get the replies. 
controller.loadNextReplies(limit: 25) { error in 
    if let error = error { 
        // handle error 
        print(error) 
    } else { 
        // access messages 
        print(controller.replies) 
         
        controller.loadNextReplies(limit: 25) { error in 
            // handle error / access messages 
            print(error ?? controller.replies) 
        } 
    } 
}

Quote Message

Copied!

Instead of replying in a thread, it's also possible to quote a message. Quoting a message doesn't result in the creation of a thread; the message is quoted inline.

To quote a message, simply provide the quoted_message_id field when sending a message:

1
2
3
4
5
6
7
8
9
// Create the initial message 
await channel.sendMessage({ id: 'first_message_id', text: 'The initial message' }); 
 
// Quote the initial message 
const res = await channel.sendMessage({ 
        id: 'message_with_quoted_message', 
        text: 'This is the first message that quotes another message', 
        quoted_message_id: 'first_message_id', 
    });
1
2
3
4
5
val message = Message( 
    text = "This message quotes another message!", 
    replyMessageId = originalMessage.id, 
) 
channelClient.sendMessage(message).enqueue { /* ... */ }
1
2
3
4
5
Message message = new Message(); 
message.setText("This message quotes another message!"); 
message.setReplyMessageId(originalMessage.getId()); 
 
channelClient.sendMessage(message).enqueue(result -> { /* ... */ });
1
2
3
4
5
6
7
8
9
// Create the initial message 
await channel.sendMessage(Message(id: 'first_message_id', text: 'The initial message' )); 
 
// Quote the initial message 
final res = await channel.sendMessage(Message( 
        id: 'message_with_quoted_message', 
        text: 'This is the first message that quotes another message', 
        quotedMessageId: 'first_message_id', 
    ));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import StreamChat 
 
/// 1: Create a `ChannelId` that represents the channel you want to send a message to. 
let channelId = ChannelId(type: .messaging, id: "general") 
 
/// 2: Use the `ChatClient` to create a `ChatChannelController` with the `ChannelId`. 
let channelController = chatClient.channelController(for: channelId) 
 
/// 3: Call `ChatChannelController.createNewMessage` with 
/// `quotedMessageId` to create the message that quotes another message. 
channelController.createNewMessage(text: "Hello!", quotedMessageId: "message-id") { result in 
    switch result { 
    case let .success(messageId): 
        print(messageId) 
    case let .failure(error): 
        print(error) 
    } 
}

Based on the provided quoted_message_id, the quoted_message field is automatically enriched when querying channels with messages. Example response:

1
2
3
4
5
6
7
8
9
{ 
    "id": "message_with_quoted_message", 
    "text": "This is the first message that quotes another message", 
    "quoted_message_id": "first_message_id", 
    "quoted_message": {  
        "id": "first_message_id",  
        "text": "The initial message" 
    } 
}