Skip to content

Commit d16844a

Browse files
authored
refactor(llc, ui): introduce 'ChannelCapability' and deprecate 'PermissionType' (#2155)
* refactor(llc, ui): introduce 'ChannelCapability' and deprecate 'PermissionType' * chore: update CHANGELOG.md * test: add channel capability extension tests * Apply suggestions from code review * chore: minor changes
1 parent 2546a54 commit d16844a

File tree

13 files changed

+635
-48
lines changed

13 files changed

+635
-48
lines changed

packages/stream_chat/CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
## Upcoming
2+
3+
✅ Added
4+
5+
- Added new helper extensions on `Channel` to provide a convenient way to check if the current user
6+
has specific capabilities in a channel.
7+
8+
```dart
9+
final canSendMessage = channel.canSendMessage;
10+
final canSendReaction = channel.canSendReaction;
11+
```
12+
13+
🔄 Changed
14+
15+
- Deprecated `PermissionType` in favor of `ChannelCapability`.
16+
117
## 9.6.0
218

319
🐞 Fixed

packages/stream_chat/lib/src/client/channel.dart

Lines changed: 188 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ class Channel {
270270
final userLastMessageAt = currentUserLastMessageAt;
271271
if (userLastMessageAt == null) return 0;
272272

273-
if (ownCapabilities.contains(PermissionType.skipSlowMode)) return 0;
273+
if (canSkipSlowMode) return 0;
274274

275275
final currentTime = DateTime.timestamp();
276276
final elapsedTime = currentTime.difference(userLastMessageAt).inSeconds;
@@ -413,11 +413,11 @@ class Channel {
413413
}
414414

415415
/// List of user permissions on this channel
416-
List<String> get ownCapabilities =>
416+
List<ChannelCapability> get ownCapabilities =>
417417
state?._channelState.channel?.ownCapabilities ?? [];
418418

419419
/// List of user permissions on this channel
420-
Stream<List<String>> get ownCapabilitiesStream {
420+
Stream<List<ChannelCapability>> get ownCapabilitiesStream {
421421
_checkInitialized();
422422
return state!.channelStateStream
423423
.map((cs) => cs.channel?.ownCapabilities ?? [])
@@ -2899,9 +2899,7 @@ class ChannelClientState {
28992899
if (_channel.isMuted) return false;
29002900

29012901
// Don't count if the channel doesn't allow read events.
2902-
if (!_channel.ownCapabilities.contains(PermissionType.readEvents)) {
2903-
return false;
2904-
}
2902+
if (!_channel.canReceiveReadEvents) return false;
29052903

29062904
// Don't count thread replies which are not shown in the channel as unread.
29072905
if (message.parentId != null && message.showInChannel == false) {
@@ -3215,3 +3213,187 @@ extension on Iterable<Message> {
32153213
return messageMap.values;
32163214
}
32173215
}
3216+
3217+
/// Extension methods for checking channel capabilities on a Channel instance.
3218+
///
3219+
/// These methods provide a convenient way to check if the current user has
3220+
/// specific capabilities in a channel.
3221+
extension ChannelCapabilityCheck on Channel {
3222+
/// True, if the current user can send a message to this channel.
3223+
bool get canSendMessage {
3224+
return ownCapabilities.contains(ChannelCapability.sendMessage);
3225+
}
3226+
3227+
/// True, if the current user can send a reply to this channel.
3228+
bool get canSendReply {
3229+
return ownCapabilities.contains(ChannelCapability.sendReply);
3230+
}
3231+
3232+
/// True, if the current user can send a message with restricted visibility.
3233+
bool get canSendRestrictedVisibilityMessage {
3234+
return ownCapabilities.contains(
3235+
ChannelCapability.sendRestrictedVisibilityMessage,
3236+
);
3237+
}
3238+
3239+
/// True, if the current user can send reactions.
3240+
bool get canSendReaction {
3241+
return ownCapabilities.contains(ChannelCapability.sendReaction);
3242+
}
3243+
3244+
/// True, if the current user can attach links to messages.
3245+
bool get canSendLinks {
3246+
return ownCapabilities.contains(ChannelCapability.sendLinks);
3247+
}
3248+
3249+
/// True, if the current user can attach files to messages.
3250+
bool get canCreateAttachment {
3251+
return ownCapabilities.contains(ChannelCapability.createAttachment);
3252+
}
3253+
3254+
/// True, if the current user can freeze or unfreeze channel.
3255+
bool get canFreezeChannel {
3256+
return ownCapabilities.contains(ChannelCapability.freezeChannel);
3257+
}
3258+
3259+
/// True, if the current user can enable or disable slow mode.
3260+
bool get canSetChannelCooldown {
3261+
return ownCapabilities.contains(ChannelCapability.setChannelCooldown);
3262+
}
3263+
3264+
/// True, if the current user can leave channel (remove own membership).
3265+
bool get canLeaveChannel {
3266+
return ownCapabilities.contains(ChannelCapability.leaveChannel);
3267+
}
3268+
3269+
/// True, if the current user can join channel (add own membership).
3270+
bool get canJoinChannel {
3271+
return ownCapabilities.contains(ChannelCapability.joinChannel);
3272+
}
3273+
3274+
/// True, if the current user can pin a message.
3275+
bool get canPinMessage {
3276+
return ownCapabilities.contains(ChannelCapability.pinMessage);
3277+
}
3278+
3279+
/// True, if the current user can delete any message from the channel.
3280+
bool get canDeleteAnyMessage {
3281+
return ownCapabilities.contains(ChannelCapability.deleteAnyMessage);
3282+
}
3283+
3284+
/// True, if the current user can delete own messages from the channel.
3285+
bool get canDeleteOwnMessage {
3286+
return ownCapabilities.contains(ChannelCapability.deleteOwnMessage);
3287+
}
3288+
3289+
/// True, if the current user can update any message in the channel.
3290+
bool get canUpdateAnyMessage {
3291+
return ownCapabilities.contains(ChannelCapability.updateAnyMessage);
3292+
}
3293+
3294+
/// True, if the current user can update own messages in the channel.
3295+
bool get canUpdateOwnMessage {
3296+
return ownCapabilities.contains(ChannelCapability.updateOwnMessage);
3297+
}
3298+
3299+
/// True, if the current user can use message search.
3300+
bool get canSearchMessages {
3301+
return ownCapabilities.contains(ChannelCapability.searchMessages);
3302+
}
3303+
3304+
/// True, if the current user can send typing events.
3305+
bool get canSendTypingEvents {
3306+
return ownCapabilities.contains(ChannelCapability.sendTypingEvents);
3307+
}
3308+
3309+
/// True, if the current user can upload message attachments.
3310+
bool get canUploadFile {
3311+
return ownCapabilities.contains(ChannelCapability.uploadFile);
3312+
}
3313+
3314+
/// True, if the current user can delete channel.
3315+
bool get canDeleteChannel {
3316+
return ownCapabilities.contains(ChannelCapability.deleteChannel);
3317+
}
3318+
3319+
/// True, if the current user can update channel data.
3320+
bool get canUpdateChannel {
3321+
return ownCapabilities.contains(ChannelCapability.updateChannel);
3322+
}
3323+
3324+
/// True, if the current user can update channel members.
3325+
bool get canUpdateChannelMembers {
3326+
return ownCapabilities.contains(ChannelCapability.updateChannelMembers);
3327+
}
3328+
3329+
/// True, if the current user can update thread data.
3330+
bool get canUpdateThread {
3331+
return ownCapabilities.contains(ChannelCapability.updateThread);
3332+
}
3333+
3334+
/// True, if the current user can quote a message.
3335+
bool get canQuoteMessage {
3336+
return ownCapabilities.contains(ChannelCapability.quoteMessage);
3337+
}
3338+
3339+
/// True, if the current user can ban channel members.
3340+
bool get canBanChannelMembers {
3341+
return ownCapabilities.contains(ChannelCapability.banChannelMembers);
3342+
}
3343+
3344+
/// True, if the current user can flag a message.
3345+
bool get canFlagMessage {
3346+
return ownCapabilities.contains(ChannelCapability.flagMessage);
3347+
}
3348+
3349+
/// True, if the current user can mute a channel.
3350+
bool get canMuteChannel {
3351+
return ownCapabilities.contains(ChannelCapability.muteChannel);
3352+
}
3353+
3354+
/// True, if the current user can send custom events.
3355+
bool get canSendCustomEvents {
3356+
return ownCapabilities.contains(ChannelCapability.sendCustomEvents);
3357+
}
3358+
3359+
/// True, if the current user has read events capability.
3360+
bool get canReceiveReadEvents {
3361+
return ownCapabilities.contains(ChannelCapability.readEvents);
3362+
}
3363+
3364+
/// True, if the current user has connect events capability.
3365+
bool get canReceiveConnectEvents {
3366+
return ownCapabilities.contains(ChannelCapability.connectEvents);
3367+
}
3368+
3369+
/// True, if the current user can send and receive typing events.
3370+
bool get canUseTypingEvents {
3371+
return ownCapabilities.contains(ChannelCapability.typingEvents);
3372+
}
3373+
3374+
/// True, if channel slow mode is active.
3375+
bool get isInSlowMode {
3376+
return ownCapabilities.contains(ChannelCapability.slowMode);
3377+
}
3378+
3379+
/// True, if the current user is allowed to post messages as usual even if the
3380+
/// channel is in slow mode.
3381+
bool get canSkipSlowMode {
3382+
return ownCapabilities.contains(ChannelCapability.skipSlowMode);
3383+
}
3384+
3385+
/// True, if the current user can create a poll.
3386+
bool get canSendPoll {
3387+
return ownCapabilities.contains(ChannelCapability.sendPoll);
3388+
}
3389+
3390+
/// True, if the current user can vote in a poll.
3391+
bool get canCastPollVote {
3392+
return ownCapabilities.contains(ChannelCapability.castPollVote);
3393+
}
3394+
3395+
/// True, if the current user can query poll votes.
3396+
bool get canQueryPollVotes {
3397+
return ownCapabilities.contains(ChannelCapability.queryPollVotes);
3398+
}
3399+
}

packages/stream_chat/lib/src/core/models/channel_model.dart

Lines changed: 119 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class ChannelModel {
1414
String? id,
1515
String? type,
1616
String? cid,
17-
this.ownCapabilities,
17+
List<String>? ownCapabilities,
1818
ChannelConfig? config,
1919
this.createdBy,
2020
this.frozen = false,
@@ -40,6 +40,7 @@ class ChannelModel {
4040
config = config ?? ChannelConfig(),
4141
createdAt = createdAt ?? DateTime.now(),
4242
updatedAt = updatedAt ?? DateTime.now(),
43+
ownCapabilities = ownCapabilities?.map(ChannelCapability.new).toList(),
4344

4445
// For backwards compatibility, set 'disabled', 'hidden'
4546
// and 'truncated_at' in [extraData].
@@ -67,9 +68,9 @@ class ChannelModel {
6768
@JsonKey(includeToJson: false)
6869
final String cid;
6970

70-
/// List of user permissions on this channel
71+
/// List of various capabilities that a user can have in a channel.
7172
@JsonKey(includeToJson: false)
72-
final List<String>? ownCapabilities;
73+
final List<ChannelCapability>? ownCapabilities;
7374

7475
/// The channel configuration data
7576
@JsonKey(includeToJson: false)
@@ -238,3 +239,118 @@ class ChannelModel {
238239
);
239240
}
240241
}
242+
243+
/// {@template channelCapability}
244+
/// Represents various capabilities that a user can have in a channel.
245+
/// {@endtemplate}
246+
extension type const ChannelCapability(String capability) implements String {
247+
/// Ability to send a message.
248+
static const sendMessage = ChannelCapability('send-message');
249+
250+
/// Ability to reply to a message.
251+
static const sendReply = ChannelCapability('send-reply');
252+
253+
/// Ability to send a message with restricted visibility.
254+
static const sendRestrictedVisibilityMessage = ChannelCapability(
255+
'send-restricted-visibility-message',
256+
);
257+
258+
/// Ability to send reactions.
259+
static const sendReaction = ChannelCapability('send-reaction');
260+
261+
/// Ability to attach links to messages.
262+
static const sendLinks = ChannelCapability('send-links');
263+
264+
/// Ability to attach files to messages.
265+
static const createAttachment = ChannelCapability('create-attachment');
266+
267+
/// Ability to freeze or unfreeze channel.
268+
static const freezeChannel = ChannelCapability('freeze-channel');
269+
270+
/// Ability to enable or disable slow mode.
271+
static const setChannelCooldown = ChannelCapability('set-channel-cooldown');
272+
273+
/// Ability to leave channel (remove own membership).
274+
static const leaveChannel = ChannelCapability('leave-channel');
275+
276+
/// Ability to join channel (add own membership).
277+
static const joinChannel = ChannelCapability('join-channel');
278+
279+
/// Ability to pin a message.
280+
static const pinMessage = ChannelCapability('pin-message');
281+
282+
/// Ability to delete any message from the channel.
283+
static const deleteAnyMessage = ChannelCapability('delete-any-message');
284+
285+
/// Ability to delete own messages from the channel.
286+
static const deleteOwnMessage = ChannelCapability('delete-own-message');
287+
288+
/// Ability to update any message in the channel.
289+
static const updateAnyMessage = ChannelCapability('update-any-message');
290+
291+
/// Ability to update own messages in the channel.
292+
static const updateOwnMessage = ChannelCapability('update-own-message');
293+
294+
/// Ability to use message search.
295+
static const searchMessages = ChannelCapability('search-messages');
296+
297+
/// Ability to send typing events.
298+
static const sendTypingEvents = ChannelCapability('send-typing-events');
299+
300+
/// Ability to upload message attachments.
301+
static const uploadFile = ChannelCapability('upload-file');
302+
303+
/// Ability to delete channel.
304+
static const deleteChannel = ChannelCapability('delete-channel');
305+
306+
/// Ability to update channel data.
307+
static const updateChannel = ChannelCapability('update-channel');
308+
309+
/// Ability to update channel members.
310+
static const updateChannelMembers = ChannelCapability(
311+
'update-channel-members',
312+
);
313+
314+
/// Ability to update thread data.
315+
static const updateThread = ChannelCapability('update-thread');
316+
317+
/// Ability to quote a message.
318+
static const quoteMessage = ChannelCapability('quote-message');
319+
320+
/// Ability to ban channel members.
321+
static const banChannelMembers = ChannelCapability('ban-channel-members');
322+
323+
/// Ability to flag a message.
324+
static const flagMessage = ChannelCapability('flag-message');
325+
326+
/// Ability to mute a channel.
327+
static const muteChannel = ChannelCapability('mute-channel');
328+
329+
/// Ability to send custom events.
330+
static const sendCustomEvents = ChannelCapability('send-custom-events');
331+
332+
/// Ability to receive read events.
333+
static const readEvents = ChannelCapability('read-events');
334+
335+
/// Ability to receive connect events.
336+
static const connectEvents = ChannelCapability('connect-events');
337+
338+
/// Ability to send and receive typing events.
339+
static const typingEvents = ChannelCapability('typing-events');
340+
341+
/// Indicates that channel slow mode is active.
342+
static const slowMode = ChannelCapability('slow-mode');
343+
344+
/// Indicates that the user is allowed to post messages as usual even if the
345+
/// channel is in slow mode.
346+
static const skipSlowMode = ChannelCapability('skip-slow-mode');
347+
348+
/// Ability to update a poll.
349+
static const sendPoll = ChannelCapability('send-poll');
350+
351+
/// Ability to update a poll.
352+
static const castPollVote = ChannelCapability('cast-poll-vote');
353+
354+
/// Ability to query poll votes.
355+
static const queryPollVotes = ChannelCapability('query-poll-votes');
356+
}

packages/stream_chat/lib/src/permission_type.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/// Describes capabilities of a user vis-a-vis a channel
2+
@Deprecated("Use 'ChannelCapability' instead")
23
class PermissionType {
34
/// Capability required to send a message in the channel
45
/// Channel is not frozen (or user has UseFrozenChannel permission)

0 commit comments

Comments
 (0)