Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/stream_chat/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Added user-group APIs on `StreamChatClient`: `listUserGroups`, `searchUserGroups`, `getUserGroup`, `createUserGroup`, `updateUserGroup`, `deleteUserGroup`, `addUserGroupMembers`, `removeUserGroupMembers`.
- Added enhanced-mention fields on `Message`: `mentionedChannel`, `mentionedHere`, `mentionedRoles`, `mentionedGroupIds`, and `mentionedGroups`.
- Added `ChannelCapability.notifyChannel`, `notifyHere`, `notifyRole`, and `notifyGroup` capabilities, with matching `Channel.canNotifyChannel` / `canNotifyHere` / `canNotifyRole` / `canNotifyGroup` getters.
- Added `ChannelCapability.createMention` capability with a matching `Channel.canCreateMention` getter.
- Added `ChannelConfig.pushLevel`, `pushNotifications`, and `chatPreferences` for per-channel-type push configuration.
- Added `PushLevel` and `ChatPreferenceLevel` extension types and the `ChatPreferences` model — granular per-category push preferences (direct, channel, here, role, group mentions, thread replies, default).
- Added `chatPreferences` field to `PushPreferenceInput`, `PushPreference`, and `ChannelPushPreference`.
Expand Down
5 changes: 5 additions & 0 deletions packages/stream_chat/lib/src/client/channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4589,4 +4589,9 @@ extension ChannelCapabilityCheck on Channel {
bool get canNotifyGroup {
return ownCapabilities.contains(ChannelCapability.notifyGroup);
}

/// True, if the current user can mention a user in a message.
bool get canCreateMention {
return ownCapabilities.contains(ChannelCapability.createMention);
}
}
3 changes: 3 additions & 0 deletions packages/stream_chat/lib/src/core/models/channel_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -407,4 +407,7 @@ extension type const ChannelCapability(String capability) implements String {

/// Ability to mention one or more user groups in a message.
static const notifyGroup = ChannelCapability('notify-group');

/// Ability to mention a user in a message.
static const createMention = ChannelCapability('create-mention');
}
6 changes: 6 additions & 0 deletions packages/stream_chat/test/src/client/channel_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8529,6 +8529,12 @@ void main() {
(channel) => channel.canNotifyGroup,
);

testCapability(
'CreateMentions',
ChannelCapability.createMention,
(channel) => channel.canCreateMention,
);

test('returns correct values with multiple capabilities', () {
final channelState = _generateChannelState(
channelId,
Expand Down
1 change: 1 addition & 0 deletions packages/stream_chat_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
✅ Added

- Added support for `@channel`, `@here`, role, and user-group mentions — parsed on incoming messages, rendered as styled tappable spans in message text, and selectable from the composer's `@` autocomplete.
- `StreamMentionAutocompleteOptions` now hides user suggestions when the channel lacks the `create-mention` capability, skipping the `queryMembers` / `queryUsers` calls.
- Added a single `mentionItemBuilder` on `StreamMessageComposer` and `StreamMentionAutocompleteOptions` that receives `StreamMentionItemProps` and covers every mention kind. Customise globally via `streamChatComponentBuilders(mentionItem: ...)` or per-instance via the new constructor parameter. Defaults are rendered by `DefaultStreamMentionItem`. Also added `onMention*Tap` callbacks on `StreamMentionAutocompleteOptions`.
- Added `StreamMessageListView.onMentionTap` and `StreamMessageItem.onMentionTap` — receives a typed `StreamMention` (`StreamUserMention`, `StreamChannelMention`, `StreamHereMention`, `StreamRoleMention`, or `StreamGroupMention`).
- `StreamMessageComposer` now surfaces the hold-to-record hint through `StreamSnackbar` anchored above the composer, and `StreamChat` provides an app-wide `StreamSnackbarScope` fallback.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ class _StreamMentionAutocompleteOptionsState extends State<StreamMentionAutocomp
}

Future<List<User>> _fetchUsers(String query) async {
if (!widget.channel.canCreateMention) return const [];
try {
if (widget.mentionAllAppUsers) {
return await _queryUsers(query);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,11 +511,39 @@ void main() {
user: User(id: id, name: name),
);

testWidgets('query does not call queryUser or queryMembers when createMention capability is missing', (
tester,
) async {
final mocks = _setupMocks(ownCapabilities: const []);

await _pumpMentionOptions(
tester,
client: mocks.client,
channel: mocks.channel,
query: 'ali',
);

verifyNever(
() => mocks.channel.queryMembers(
filter: any(named: 'filter'),
pagination: any(named: 'pagination'),
),
);
verifyNever(
() => mocks.client.queryUsers(
presence: any(named: 'presence'),
filter: any(named: 'filter'),
sort: any(named: 'sort'),
pagination: any(named: 'pagination'),
),
);
});

testWidgets(
'all members cached use in-memory search and skip queryMembers API call',
(tester) async {
final mocks = _setupMocks(
ownCapabilities: const [],
ownCapabilities: const [ChannelCapability.createMention],
members: [
buildMember('alice-id', 'Alice'),
buildMember('bob-id', 'Bob'),
Expand Down Expand Up @@ -546,7 +574,7 @@ void main() {
'partial cache (memberCount > cached members) triggers queryMembers API call',
(tester) async {
final mocks = _setupMocks(
ownCapabilities: const [],
ownCapabilities: const [ChannelCapability.createMention],
members: [
buildMember('user-1', 'User 1'),
buildMember('user-2', 'User 2'),
Expand Down Expand Up @@ -583,7 +611,7 @@ void main() {
'null memberCount falls back to remote queryMembers',
(tester) async {
final mocks = _setupMocks(
ownCapabilities: const [],
ownCapabilities: const [ChannelCapability.createMention],
members: [buildMember('cached-id', 'Cached')],
memberCount: null,
);
Expand Down Expand Up @@ -618,7 +646,7 @@ void main() {
'local search includes watchers',
(tester) async {
final mocks = _setupMocks(
ownCapabilities: const [],
ownCapabilities: const [ChannelCapability.createMention],
members: [buildMember('alice-id', 'Alice')],
watchers: [User(id: 'wally-id', name: 'Wally')],
);
Expand All @@ -638,7 +666,7 @@ void main() {
'local results render alphabetically by name',
(tester) async {
final mocks = _setupMocks(
ownCapabilities: const [],
ownCapabilities: const [ChannelCapability.createMention],
members: [
buildMember('c-id', 'Charlie'),
buildMember('a-id', 'Alice'),
Expand All @@ -665,7 +693,7 @@ void main() {
'remote queryMembers results render in server order',
(tester) async {
final mocks = _setupMocks(
ownCapabilities: const [],
ownCapabilities: const [ChannelCapability.createMention],
members: [buildMember('cached-id', 'Cached')],
memberCount: 50,
);
Expand Down Expand Up @@ -701,7 +729,7 @@ void main() {
testWidgets(
'mentionAllAppUsers calls client.queryUsers instead of channel.queryMembers',
(tester) async {
final mocks = _setupMocks(ownCapabilities: const []);
final mocks = _setupMocks(ownCapabilities: const [ChannelCapability.createMention]);
when(
() => mocks.client.queryUsers(
presence: any(named: 'presence'),
Expand Down Expand Up @@ -743,7 +771,10 @@ void main() {
'queryMembers error is swallowed and does not blank the list',
(tester) async {
final mocks = _setupMocks(
ownCapabilities: const [ChannelCapability.notifyHere],
ownCapabilities: const [
ChannelCapability.notifyHere,
ChannelCapability.createMention,
],
members: [buildMember('cached-id', 'Cached')],
memberCount: 50,
);
Expand Down Expand Up @@ -775,6 +806,7 @@ void main() {
ChannelCapability.notifyHere,
ChannelCapability.notifyRole,
ChannelCapability.notifyGroup,
ChannelCapability.createMention,
],
members: [
Member(
Expand Down Expand Up @@ -858,6 +890,7 @@ void main() {
ChannelCapability.notifyHere,
ChannelCapability.notifyRole,
ChannelCapability.notifyGroup,
ChannelCapability.createMention,
],
members: [
Member(
Expand Down
Loading