Typing Indicators

LAST EDIT Feb 17 2021

All official SDKs support typing events out of the box and are handled out of the box on all channels with the typing_events featured enabled. Typing indicators allow you to show to users who is currently typing in the channel. This feature can be switched on/off on a channel-type basis using the CLI or directly from the Dashboard. If you are using one of the official SDK libraries, you will only need to ensure that typing indicators are enabled to get this working.

If you are building your UI on top of one of our Chat Clients instead, you will need to take care of four things:

  1. Send an event typing.start when the user starts typing

  2. Send an event typing.stop after the user stopped typing

  3. Handle the two events and use them to toggle the typing indicator UI

  4. Use parent_id field of the event to indicate that typing is happening in a thread

Sending start and stop typing events

Copied!
1
2
3
4
5
6
7
8
9
10
11
12
// The JS client keeps track of the typing state for you. 
// Just call `channel.keystroke()` when the user types and  
// `channel.stopTyping()` when the user sends the message (or aborts) 
 
// sends a typing.start event at most once every two seconds 
await channel.keystroke(); 
 
// sends a typing.start event for a particular thread 
await channel.keystroke(thread_id); 
 
// sends the typing.stop event  
await channel.stopTyping();
1
2
3
4
5
6
7
8
9
10
11
12
// The Dart client keeps track of the typing state for you. 
// Just call `channel.keystroke()` when the user types and  
// `channel.stopTyping()` when the user sends the message (or aborts) 
 
// sends a typing.start event at most once every two seconds 
await channel.keystroke(); 
 
// sends a typing.start event for a particular thread 
await channel.keystroke(thread_id); 
 
// sends the typing.stop event  
await channel.stopTyping();
1
2
3
4
5
6
7
8
// Sends a typing.start event at most once every two seconds 
channelClient.keystroke().enqueue() 
 
// Sends a typing.start event for a particular thread 
channelClient.keystroke(parentId = "threadId").enqueue() 
 
// Sends the typing.stop event 
channelClient.stopTyping().enqueue()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let channel = Client.shared.channel(type: .messaging, id: "general") 
 
// Automatically 
// The Swift client keeps track of the typing state for you. 
// Just call `channel.keystroke()` when the user types and  
 
// sends a typing.start event at most once every two seconds 
// automatically sends typingStop event after 15 seconds 
channel.keystroke { result in 
    // handle result 
} 
 
// or Manually 
channel.send(eventType: .typingStart) { result in 
    // handle result 
} 
 
channel.send(eventType: .typingStop) { result in 
    // handle result 
}
1
2
3
4
5
6
7
8
// Sends a typing.start event 
channelClient.keystroke().enqueue(); 
 
// Sends a typing.start event for a particular thread 
channelClient.keystroke("threadId").enqueue(); 
 
// Sends the typing.stop event 
channelClient.stopTyping().enqueue();

When sending events on user input, you should make sure to follow some best-practices to avoid bugs.

  1. Only send typing.start when the user starts typing

  2. Send typing.stop after a few seconds since the last keystroke

Receiving typing indicator events

Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// channels keep track of the users that are currently typing. 
// `channel.state.typing` is an immutable object which gets regenerated 
// every time a new user is added or removed to this list 
console.log(channel.state.typing); 
 
// start typing event handling 
channel.on('typing.start', event => { 
  if (event.parent_id) { 
    console.log(`${event.user.name} started typing in thread ${event.parent_id}`); 
  } else { 
    console.log(`${event.user.name} started typing`); 
  } 
}); 
 
// stop typing event handling 
channel.on('typing.stop', event => { 
  if (event.parent_id) { 
    console.log(`${event.user.name} stopped typing in thread ${event.parent_id}`); 
  } else { 
    console.log(`${event.user.name} stopped typing`); 
  } 
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// channels keep track of the users that are currently typing 
// the `channel.state.typingEvents` is an immutable object which gets regenerated 
// every time a new user is added or removed to this list 
print(channel.state.typingEvents); 
 
// add typing start event handling 
channel.on('typing.start', (event) => { 
  print('${event.user.name} started typing'); 
}); 
 
// add typing stop event handling 
channel.on('typing.stop', event => { 
  print('${event.user.name} stopped typing'); 
});
1
2
3
4
5
6
7
8
9
// Add typing start event handling 
channelClient.subscribeFor<TypingStartEvent> { typingStartEvent -> 
    // Handle event 
} 
 
// Add typing stop event handling 
channelClient.subscribeFor<TypingStopEvent> { typingStopEvent -> 
    // Handle event 
}
1
2
3
4
5
6
7
8
let channel = Client.shared.channel(type: .messaging, id: "general") 
 
let subscription = channel.subscribe(forEvents: [.typingStart, .typingStop]) { event in 
    // handle event 
} 
 
// Cancel subscription when you want to stop receiving events 
subscription.cancel()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Add typing start event handling 
channelClient.subscribeFor( 
        new Class[]{TypingStartEvent.class}, 
        event -> { 
            // Handle change 
        } 
); 
 
// Add typing stop event handling 
channelClient.subscribeFor( 
        new Class[]{TypingStopEvent.class}, 
        event -> { 
            // Handle change 
        } 
);
Because clients might fail at sending typing.stop event all Chat clients periodically prune the list of typing users.