1
+ // ignore_for_file: avoid_redundant_argument_values
2
+
1
3
import 'dart:async' ;
2
4
import 'dart:math' ;
3
5
4
6
import 'package:collection/collection.dart' ;
5
7
import 'package:rxdart/rxdart.dart' ;
6
8
import 'package:stream_chat/src/client/retry_queue.dart' ;
7
- import 'package:stream_chat/src/core/models/banned_user.dart' ;
8
9
import 'package:stream_chat/src/core/util/utils.dart' ;
9
10
import 'package:stream_chat/stream_chat.dart' ;
10
11
import 'package:synchronized/synchronized.dart' ;
@@ -1017,6 +1018,35 @@ class Channel {
1017
1018
},
1018
1019
);
1019
1020
1021
+ /// Creates or updates a new [draft] for this channel.
1022
+ Future <CreateDraftResponse > createDraft (
1023
+ DraftMessage draft,
1024
+ ) {
1025
+ _checkInitialized ();
1026
+ return _client.createDraft (draft, id! , type);
1027
+ }
1028
+
1029
+ /// Retrieves the draft for this channel.
1030
+ ///
1031
+ /// Optionally, provide a [parentId] to get the draft for a specific thread.
1032
+ Future <GetDraftResponse > getDraft ({
1033
+ String ? parentId,
1034
+ }) {
1035
+ _checkInitialized ();
1036
+ return _client.getDraft (id! , type, parentId: parentId);
1037
+ }
1038
+
1039
+ /// Deletes the draft for this channel.
1040
+ ///
1041
+ /// Optionally, provide a [parentId] to delete the draft for a specific
1042
+ /// thread.
1043
+ Future <EmptyResponse > deleteDraft ({
1044
+ String ? parentId,
1045
+ }) {
1046
+ _checkInitialized ();
1047
+ return _client.deleteDraft (id! , type, parentId: parentId);
1048
+ }
1049
+
1020
1050
/// Send a file to this channel.
1021
1051
Future <SendFileResponse > sendFile (
1022
1052
AttachmentFile file, {
@@ -2010,6 +2040,14 @@ class ChannelClientState {
2010
2040
2011
2041
_listenMessageUpdated ();
2012
2042
2043
+ /* Start of draft events */
2044
+
2045
+ _listenDraftUpdated ();
2046
+
2047
+ _listenDraftDeleted ();
2048
+
2049
+ /* End of draft events */
2050
+
2013
2051
_listenReactions ();
2014
2052
2015
2053
_listenReactionDeleted ();
@@ -2515,6 +2553,48 @@ class ChannelClientState {
2515
2553
}));
2516
2554
}
2517
2555
2556
+ void _listenDraftUpdated () {
2557
+ _subscriptions.add (
2558
+ _channel.on (EventType .draftUpdated).listen ((event) {
2559
+ final draft = event.draft;
2560
+ if (draft == null ) return ;
2561
+
2562
+ if (draft.parentId case final parentId? ) {
2563
+ for (final message in messages) {
2564
+ if (message.id == parentId) {
2565
+ return updateMessage (message.copyWith (draft: draft));
2566
+ }
2567
+ }
2568
+ }
2569
+
2570
+ updateChannelState (channelState.copyWith (draft: draft));
2571
+ }),
2572
+ );
2573
+ }
2574
+
2575
+ void _listenDraftDeleted () {
2576
+ _subscriptions.add (
2577
+ _channel.on (EventType .draftDeleted).listen ((event) {
2578
+ final draft = event.draft;
2579
+ if (draft == null ) return ;
2580
+
2581
+ if (draft.parentId case final parentId? ) {
2582
+ for (final message in messages) {
2583
+ if (message.id == parentId) {
2584
+ return updateMessage (
2585
+ message.copyWith (draft: null ),
2586
+ );
2587
+ }
2588
+ }
2589
+ }
2590
+
2591
+ updateChannelState (
2592
+ channelState.copyWith (draft: null ),
2593
+ );
2594
+ }),
2595
+ );
2596
+ }
2597
+
2518
2598
void _listenReactionDeleted () {
2519
2599
_subscriptions.add (_channel.on (EventType .reactionDeleted).listen ((event) {
2520
2600
final oldMessage =
@@ -2699,8 +2779,8 @@ class ChannelClientState {
2699
2779
}
2700
2780
2701
2781
// If the message is part of a thread, update thread information.
2702
- if (message.parentId != null ) {
2703
- updateThreadInfo (message. parentId! , [message]);
2782
+ if (message.parentId case final parentId ? ) {
2783
+ updateThreadInfo (parentId, [message]);
2704
2784
}
2705
2785
}
2706
2786
@@ -2920,6 +3000,14 @@ class ChannelClientState {
2920
3000
(watchers, users) => [...? watchers? .map ((e) => users[e.id] ?? e)],
2921
3001
).distinct (const ListEquality ().equals);
2922
3002
3003
+ /// Channel draft.
3004
+ Draft ? get draft => _channelState.draft;
3005
+
3006
+ /// Channel draft as a stream.
3007
+ Stream <Draft ?> get draftStream {
3008
+ return channelStateStream.map ((cs) => cs.draft).distinct ();
3009
+ }
3010
+
2923
3011
/// Channel member for the current user.
2924
3012
Member ? get currentUserMember => members.firstWhereOrNull (
2925
3013
(m) => m.user? .id == _channel.client.state.currentUser? .id,
@@ -3019,24 +3107,6 @@ class ChannelClientState {
3019
3107
return count;
3020
3108
}
3021
3109
3022
- /// Update threads with updated information about messages.
3023
- void updateThreadInfo (String parentId, List <Message > messages) {
3024
- final newThreads = Map <String , List <Message >>.from (threads);
3025
-
3026
- if (newThreads.containsKey (parentId)) {
3027
- newThreads[parentId] = [
3028
- ...messages,
3029
- ...newThreads[parentId]! .where (
3030
- (newMessage) => ! messages.any ((m) => m.id == newMessage.id),
3031
- ),
3032
- ].sorted (_sortByCreatedAt);
3033
- } else {
3034
- newThreads[parentId] = messages;
3035
- }
3036
-
3037
- _threads = newThreads;
3038
- }
3039
-
3040
3110
/// Delete all channel messages.
3041
3111
void truncate () {
3042
3112
_channelState = _channelState.copyWith (
@@ -3087,6 +3157,7 @@ class ChannelClientState {
3087
3157
members: newMembers,
3088
3158
membership: updatedState.membership,
3089
3159
read: newReads,
3160
+ draft: updatedState.draft,
3090
3161
pinnedMessages: updatedState.pinnedMessages,
3091
3162
);
3092
3163
}
@@ -3112,15 +3183,11 @@ class ChannelClientState {
3112
3183
}
3113
3184
3114
3185
/// The channel threads related to this channel.
3115
- Map <String , List <Message >> get threads =>
3116
- _threadsController.value.map (MapEntry .new );
3186
+ Map <String , List <Message >> get threads => {..._threadsController.value};
3117
3187
3118
3188
/// The channel threads related to this channel as a stream.
3119
- Stream <Map <String , List <Message >>> get threadsStream =>
3120
- _threadsController.stream;
3121
- final BehaviorSubject <Map <String , List <Message >>> _threadsController =
3122
- BehaviorSubject .seeded ({});
3123
-
3189
+ Stream <Map <String , List <Message >>> get threadsStream => _threadsController;
3190
+ final _threadsController = BehaviorSubject .seeded (< String , List <Message >> {});
3124
3191
set _threads (Map <String , List <Message >> threads) {
3125
3192
_threadsController.safeAdd (threads);
3126
3193
_channel.client.chatPersistenceClient? .updateChannelThreads (
@@ -3129,6 +3196,38 @@ class ChannelClientState {
3129
3196
);
3130
3197
}
3131
3198
3199
+ /// Update threads with updated information about messages.
3200
+ void updateThreadInfo (String parentId, List <Message > messages) {
3201
+ final newThreads = {...threads}..update (
3202
+ parentId,
3203
+ (original) => < Message > [
3204
+ ...original.merge (
3205
+ messages,
3206
+ key: (message) => message.id,
3207
+ update: (original, updated) => updated.syncWith (original),
3208
+ ),
3209
+ ].sorted (_sortByCreatedAt),
3210
+ ifAbsent: () => messages.sorted (_sortByCreatedAt),
3211
+ );
3212
+
3213
+ _threads = newThreads;
3214
+ }
3215
+
3216
+ Draft ? _getThreadDraft (String parentId, List <Message >? messages) {
3217
+ return messages? .firstWhereOrNull ((it) => it.id == parentId)? .draft;
3218
+ }
3219
+
3220
+ /// Draft for a specific thread identified by [parentId] .
3221
+ Draft ? threadDraft (String parentId) => _getThreadDraft (parentId, messages);
3222
+
3223
+ /// Stream of draft for a specific thread identified by [parentId] .
3224
+ ///
3225
+ /// This stream emits a new value whenever the draft associated with the
3226
+ /// specified thread is updated or removed.
3227
+ Stream <Draft ?> threadDraftStream (String parentId) => channelStateStream
3228
+ .map ((cs) => _getThreadDraft (parentId, cs.messages))
3229
+ .distinct ();
3230
+
3132
3231
/// Channel related typing users stream.
3133
3232
Stream <Map <User , Event >> get typingEventsStream =>
3134
3233
_typingEventsController.stream;
0 commit comments