1+ // ignore_for_file: avoid_redundant_argument_values
2+
13import 'dart:async' ;
24import 'dart:math' ;
35
46import 'package:collection/collection.dart' ;
57import 'package:rxdart/rxdart.dart' ;
68import 'package:stream_chat/src/client/retry_queue.dart' ;
7- import 'package:stream_chat/src/core/models/banned_user.dart' ;
89import 'package:stream_chat/src/core/util/utils.dart' ;
910import 'package:stream_chat/stream_chat.dart' ;
1011import 'package:synchronized/synchronized.dart' ;
@@ -1043,6 +1044,35 @@ class Channel {
10431044 },
10441045 );
10451046
1047+ /// Creates or updates a new [draft] for this channel.
1048+ Future <CreateDraftResponse > createDraft (
1049+ DraftMessage draft,
1050+ ) {
1051+ _checkInitialized ();
1052+ return _client.createDraft (draft, id! , type);
1053+ }
1054+
1055+ /// Retrieves the draft for this channel.
1056+ ///
1057+ /// Optionally, provide a [parentId] to get the draft for a specific thread.
1058+ Future <GetDraftResponse > getDraft ({
1059+ String ? parentId,
1060+ }) {
1061+ _checkInitialized ();
1062+ return _client.getDraft (id! , type, parentId: parentId);
1063+ }
1064+
1065+ /// Deletes the draft for this channel.
1066+ ///
1067+ /// Optionally, provide a [parentId] to delete the draft for a specific
1068+ /// thread.
1069+ Future <EmptyResponse > deleteDraft ({
1070+ String ? parentId,
1071+ }) {
1072+ _checkInitialized ();
1073+ return _client.deleteDraft (id! , type, parentId: parentId);
1074+ }
1075+
10461076 /// Send a file to this channel.
10471077 Future <SendFileResponse > sendFile (
10481078 AttachmentFile file, {
@@ -2084,6 +2114,14 @@ class ChannelClientState {
20842114
20852115 _listenMessageUpdated ();
20862116
2117+ /* Start of draft events */
2118+
2119+ _listenDraftUpdated ();
2120+
2121+ _listenDraftDeleted ();
2122+
2123+ /* End of draft events */
2124+
20872125 _listenReactions ();
20882126
20892127 _listenReactionDeleted ();
@@ -2589,6 +2627,48 @@ class ChannelClientState {
25892627 }));
25902628 }
25912629
2630+ void _listenDraftUpdated () {
2631+ _subscriptions.add (
2632+ _channel.on (EventType .draftUpdated).listen ((event) {
2633+ final draft = event.draft;
2634+ if (draft == null ) return ;
2635+
2636+ if (draft.parentId case final parentId? ) {
2637+ for (final message in messages) {
2638+ if (message.id == parentId) {
2639+ return updateMessage (message.copyWith (draft: draft));
2640+ }
2641+ }
2642+ }
2643+
2644+ updateChannelState (channelState.copyWith (draft: draft));
2645+ }),
2646+ );
2647+ }
2648+
2649+ void _listenDraftDeleted () {
2650+ _subscriptions.add (
2651+ _channel.on (EventType .draftDeleted).listen ((event) {
2652+ final draft = event.draft;
2653+ if (draft == null ) return ;
2654+
2655+ if (draft.parentId case final parentId? ) {
2656+ for (final message in messages) {
2657+ if (message.id == parentId) {
2658+ return updateMessage (
2659+ message.copyWith (draft: null ),
2660+ );
2661+ }
2662+ }
2663+ }
2664+
2665+ updateChannelState (
2666+ channelState.copyWith (draft: null ),
2667+ );
2668+ }),
2669+ );
2670+ }
2671+
25922672 void _listenReactionDeleted () {
25932673 _subscriptions.add (_channel.on (EventType .reactionDeleted).listen ((event) {
25942674 final oldMessage =
@@ -2773,8 +2853,8 @@ class ChannelClientState {
27732853 }
27742854
27752855 // If the message is part of a thread, update thread information.
2776- if (message.parentId != null ) {
2777- updateThreadInfo (message. parentId! , [message]);
2856+ if (message.parentId case final parentId ? ) {
2857+ updateThreadInfo (parentId, [message]);
27782858 }
27792859 }
27802860
@@ -2994,6 +3074,14 @@ class ChannelClientState {
29943074 (watchers, users) => [...? watchers? .map ((e) => users[e.id] ?? e)],
29953075 ).distinct (const ListEquality ().equals);
29963076
3077+ /// Channel draft.
3078+ Draft ? get draft => _channelState.draft;
3079+
3080+ /// Channel draft as a stream.
3081+ Stream <Draft ?> get draftStream {
3082+ return channelStateStream.map ((cs) => cs.draft).distinct ();
3083+ }
3084+
29973085 /// Channel member for the current user.
29983086 Member ? get currentUserMember => members.firstWhereOrNull (
29993087 (m) => m.user? .id == _channel.client.state.currentUser? .id,
@@ -3093,24 +3181,6 @@ class ChannelClientState {
30933181 return count;
30943182 }
30953183
3096- /// Update threads with updated information about messages.
3097- void updateThreadInfo (String parentId, List <Message > messages) {
3098- final newThreads = Map <String , List <Message >>.from (threads);
3099-
3100- if (newThreads.containsKey (parentId)) {
3101- newThreads[parentId] = [
3102- ...messages,
3103- ...newThreads[parentId]! .where (
3104- (newMessage) => ! messages.any ((m) => m.id == newMessage.id),
3105- ),
3106- ].sorted (_sortByCreatedAt);
3107- } else {
3108- newThreads[parentId] = messages;
3109- }
3110-
3111- _threads = newThreads;
3112- }
3113-
31143184 /// Delete all channel messages.
31153185 void truncate () {
31163186 _channelState = _channelState.copyWith (
@@ -3161,6 +3231,7 @@ class ChannelClientState {
31613231 members: newMembers,
31623232 membership: updatedState.membership,
31633233 read: newReads,
3234+ draft: updatedState.draft,
31643235 pinnedMessages: updatedState.pinnedMessages,
31653236 );
31663237 }
@@ -3186,15 +3257,11 @@ class ChannelClientState {
31863257 }
31873258
31883259 /// The channel threads related to this channel.
3189- Map <String , List <Message >> get threads =>
3190- _threadsController.value.map (MapEntry .new );
3260+ Map <String , List <Message >> get threads => {..._threadsController.value};
31913261
31923262 /// The channel threads related to this channel as a stream.
3193- Stream <Map <String , List <Message >>> get threadsStream =>
3194- _threadsController.stream;
3195- final BehaviorSubject <Map <String , List <Message >>> _threadsController =
3196- BehaviorSubject .seeded ({});
3197-
3263+ Stream <Map <String , List <Message >>> get threadsStream => _threadsController;
3264+ final _threadsController = BehaviorSubject .seeded (< String , List <Message >> {});
31983265 set _threads (Map <String , List <Message >> threads) {
31993266 _threadsController.safeAdd (threads);
32003267 _channel.client.chatPersistenceClient? .updateChannelThreads (
@@ -3203,6 +3270,38 @@ class ChannelClientState {
32033270 );
32043271 }
32053272
3273+ /// Update threads with updated information about messages.
3274+ void updateThreadInfo (String parentId, List <Message > messages) {
3275+ final newThreads = {...threads}..update (
3276+ parentId,
3277+ (original) => < Message > [
3278+ ...original.merge (
3279+ messages,
3280+ key: (message) => message.id,
3281+ update: (original, updated) => updated.syncWith (original),
3282+ ),
3283+ ].sorted (_sortByCreatedAt),
3284+ ifAbsent: () => messages.sorted (_sortByCreatedAt),
3285+ );
3286+
3287+ _threads = newThreads;
3288+ }
3289+
3290+ Draft ? _getThreadDraft (String parentId, List <Message >? messages) {
3291+ return messages? .firstWhereOrNull ((it) => it.id == parentId)? .draft;
3292+ }
3293+
3294+ /// Draft for a specific thread identified by [parentId] .
3295+ Draft ? threadDraft (String parentId) => _getThreadDraft (parentId, messages);
3296+
3297+ /// Stream of draft for a specific thread identified by [parentId] .
3298+ ///
3299+ /// This stream emits a new value whenever the draft associated with the
3300+ /// specified thread is updated or removed.
3301+ Stream <Draft ?> threadDraftStream (String parentId) => channelStateStream
3302+ .map ((cs) => _getThreadDraft (parentId, cs.messages))
3303+ .distinct ();
3304+
32063305 /// Channel related typing users stream.
32073306 Stream <Map <User , Event >> get typingEventsStream =>
32083307 _typingEventsController.stream;
0 commit comments