refactor (core <-> ui <-> persistence) with the new filter changes.
Signed-off-by: Sahil Kumar <[email protected]>
This commit is contained in:
@@ -19,19 +19,24 @@ class Channel {
|
||||
/// Create a channel client instance.
|
||||
Channel(
|
||||
this._client,
|
||||
this.type,
|
||||
this._id,
|
||||
this._extraData,
|
||||
) : _cid = _id != null ? '$type:$_id' : null {
|
||||
this._type,
|
||||
this._id, {
|
||||
Map<String, Object> extraData = const {},
|
||||
}) : _cid = _id != null ? '$_type:$_id' : null,
|
||||
_extraData = extraData {
|
||||
_client.logger.info('New Channel instance not initialized created');
|
||||
}
|
||||
|
||||
/// Create a channel client instance from a [ChannelState] object
|
||||
Channel.fromState(this._client, ChannelState channelState) : _extraData = {} {
|
||||
_cid = channelState.channel!.cid;
|
||||
_id = channelState.channel!.id;
|
||||
type = channelState.channel!.type;
|
||||
|
||||
Channel.fromState(this._client, ChannelState channelState)
|
||||
: assert(
|
||||
channelState.channel != null,
|
||||
'No channel found inside channel state',
|
||||
),
|
||||
_id = channelState.channel!.id,
|
||||
_type = channelState.channel!.type,
|
||||
_cid = channelState.channel!.cid,
|
||||
_extraData = channelState.channel!.extraData ?? {} {
|
||||
state = ChannelClientState(this, channelState);
|
||||
_initializedCompleter.complete(true);
|
||||
_client.logger.info('New Channel instance initialized created');
|
||||
@@ -41,19 +46,20 @@ class Channel {
|
||||
ChannelClientState? state;
|
||||
|
||||
/// The channel type
|
||||
String? type;
|
||||
final String _type;
|
||||
|
||||
String? _id;
|
||||
String? _cid;
|
||||
Map<String, dynamic> _extraData;
|
||||
final Map<String, dynamic> _extraData;
|
||||
|
||||
set extraData(Map<String, dynamic> extraData) {
|
||||
if (_initializedCompleter.isCompleted) {
|
||||
throw Exception(
|
||||
'Once the channel is initialized you should use channel.update '
|
||||
'to update channel data');
|
||||
throw StateError(
|
||||
'Once the channel is initialized you should use channel.update '
|
||||
'to update channel data',
|
||||
);
|
||||
}
|
||||
_extraData = extraData;
|
||||
_extraData.addAll(extraData);
|
||||
}
|
||||
|
||||
/// Returns true if the channel is muted
|
||||
@@ -179,6 +185,9 @@ class Channel {
|
||||
/// Channel id
|
||||
String? get id => state?._channelState.channel?.id ?? _id;
|
||||
|
||||
/// Channel type
|
||||
String get type => state?._channelState.channel?.type ?? _type;
|
||||
|
||||
/// Channel cid
|
||||
String? get cid => state?._channelState.channel?.cid ?? _cid;
|
||||
|
||||
@@ -193,9 +202,11 @@ class Channel {
|
||||
state?._channelState.channel?.extraData ?? _extraData;
|
||||
|
||||
/// Channel extra data as a stream
|
||||
Stream<Map<String, dynamic>?>? get extraDataStream {
|
||||
Stream<Map<String, dynamic>> get extraDataStream {
|
||||
_checkInitialized();
|
||||
return state?.channelStateStream.map((cs) => cs.channel?.extraData);
|
||||
return state!.channelStateStream.map(
|
||||
(cs) => cs.channel?.extraData ?? _extraData,
|
||||
);
|
||||
}
|
||||
|
||||
/// The main Stream chat client
|
||||
@@ -384,7 +395,7 @@ class Channel {
|
||||
message = await attachmentsUploadCompleter.future;
|
||||
}
|
||||
|
||||
final response = await _client.sendMessage(message, id!, type!);
|
||||
final response = await _client.sendMessage(message, id!, type);
|
||||
state!.addMessage(response.message);
|
||||
return response;
|
||||
} catch (error) {
|
||||
@@ -537,7 +548,7 @@ class Channel {
|
||||
return _client.sendFile(
|
||||
file,
|
||||
id!,
|
||||
type!,
|
||||
type,
|
||||
onSendProgress: onSendProgress,
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
@@ -553,7 +564,7 @@ class Channel {
|
||||
return _client.sendImage(
|
||||
file,
|
||||
id!,
|
||||
type!,
|
||||
type,
|
||||
onSendProgress: onSendProgress,
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
@@ -565,18 +576,16 @@ class Channel {
|
||||
Map<String, dynamic>? messageFilters,
|
||||
List<SortOption>? sort,
|
||||
PaginationParams? paginationParams,
|
||||
}) =>
|
||||
_client.search(
|
||||
{
|
||||
'cid': {
|
||||
r'$in': [cid],
|
||||
},
|
||||
},
|
||||
sort: sort,
|
||||
query: query,
|
||||
paginationParams: paginationParams,
|
||||
messageFilters: messageFilters,
|
||||
);
|
||||
}) {
|
||||
_checkInitialized();
|
||||
return _client.search(
|
||||
Filter.in_('cid', [cid!]),
|
||||
sort: sort,
|
||||
query: query,
|
||||
paginationParams: paginationParams,
|
||||
messageFilters: messageFilters,
|
||||
);
|
||||
}
|
||||
|
||||
/// Delete a file from this channel
|
||||
Future<EmptyResponse> deleteFile(
|
||||
@@ -587,7 +596,7 @@ class Channel {
|
||||
return _client.deleteFile(
|
||||
url,
|
||||
id!,
|
||||
type!,
|
||||
type,
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
}
|
||||
@@ -601,7 +610,7 @@ class Channel {
|
||||
return _client.deleteImage(
|
||||
url,
|
||||
id!,
|
||||
type!,
|
||||
type,
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
}
|
||||
@@ -903,7 +912,7 @@ class Channel {
|
||||
|
||||
void _initState(ChannelState channelState) {
|
||||
state = ChannelClientState(this, channelState);
|
||||
client.state.channels![cid] = this;
|
||||
client.state.channels[cid!] = this;
|
||||
if (!_initializedCompleter.isCompleted) {
|
||||
_initializedCompleter.complete(true);
|
||||
}
|
||||
@@ -1018,9 +1027,7 @@ class Channel {
|
||||
bool preferOffline = false,
|
||||
}) async {
|
||||
var path = '/channels/$type';
|
||||
if (id != null) {
|
||||
path = '$path/$id';
|
||||
}
|
||||
if (id != null) path = '$path/$id';
|
||||
path = '$path/query';
|
||||
|
||||
final payload = Map<String, dynamic>.from({
|
||||
|
||||
@@ -29,6 +29,8 @@ import 'package:stream_chat/src/platform_detector/platform_detector.dart';
|
||||
import 'package:stream_chat/version.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import 'package:stream_chat/src/models/filter.dart';
|
||||
|
||||
/// Handler function used for logging records. Function requires a single
|
||||
/// [LogRecord] as the only parameter.
|
||||
typedef LogHandlerFunction = void Function(LogRecord record);
|
||||
@@ -552,13 +554,16 @@ class StreamChatClient {
|
||||
type: EventType.connectionRecovered,
|
||||
online: true,
|
||||
));
|
||||
if (state.channels?.isNotEmpty == true) {
|
||||
if (state.channels.isNotEmpty == true) {
|
||||
// ignore: unawaited_futures
|
||||
queryChannelsOnline(filter: {
|
||||
'cid': {
|
||||
'\$in': state.channels!.keys.toList(),
|
||||
},
|
||||
}).then(
|
||||
queryChannelsOnline(
|
||||
filter: Filter.in_('cid', state.channels.keys.toList()),
|
||||
// {
|
||||
// 'cid': {
|
||||
// '\$in': state.channels!.keys.toList(),
|
||||
// },
|
||||
// }
|
||||
).then(
|
||||
(_) async {
|
||||
await resync();
|
||||
},
|
||||
@@ -641,7 +646,7 @@ class StreamChatClient {
|
||||
|
||||
/// Requests channels with a given query.
|
||||
Stream<List<Channel>> queryChannels({
|
||||
Map<String, dynamic>? filter,
|
||||
Filter? filter,
|
||||
List<SortOption<ChannelModel>>? sort,
|
||||
Map<String, dynamic>? options,
|
||||
PaginationParams paginationParams = const PaginationParams(),
|
||||
@@ -684,7 +689,7 @@ class StreamChatClient {
|
||||
|
||||
/// Requests channels with a given query from the API.
|
||||
Future<List<Channel>> queryChannelsOnline({
|
||||
required Map<String, dynamic>? filter,
|
||||
Filter? filter,
|
||||
List<SortOption<ChannelModel>>? sort,
|
||||
Map<String, dynamic>? options,
|
||||
int? messageLimit,
|
||||
@@ -764,7 +769,7 @@ class StreamChatClient {
|
||||
final updateData = _mapChannelStateToChannel(channels);
|
||||
|
||||
await _chatPersistenceClient?.updateChannelQueries(
|
||||
filter ?? {},
|
||||
filter,
|
||||
channels.map((c) => c.channel!.cid).toList(),
|
||||
clearQueryCache: paginationParams.offset == 0,
|
||||
);
|
||||
@@ -775,7 +780,7 @@ class StreamChatClient {
|
||||
|
||||
/// Requests channels with a given query from the Persistence client.
|
||||
Future<List<Channel>> queryChannelsOffline({
|
||||
required Map<String, dynamic>? filter,
|
||||
required Filter? filter,
|
||||
required List<SortOption<ChannelModel>>? sort,
|
||||
PaginationParams paginationParams = const PaginationParams(),
|
||||
}) async {
|
||||
@@ -790,10 +795,10 @@ class StreamChatClient {
|
||||
return updatedData.value;
|
||||
}
|
||||
|
||||
MapEntry<Map<String?, Channel>, List<Channel>> _mapChannelStateToChannel(
|
||||
MapEntry<Map<String, Channel>, List<Channel>> _mapChannelStateToChannel(
|
||||
List<ChannelState> channelStates,
|
||||
) {
|
||||
final channels = {...state.channels ?? {}};
|
||||
final channels = {...state.channels};
|
||||
final newChannels = <Channel>[];
|
||||
for (final channelState in channelStates) {
|
||||
final channel = channels[channelState.channel!.cid];
|
||||
@@ -802,7 +807,7 @@ class StreamChatClient {
|
||||
newChannels.add(channel);
|
||||
} else {
|
||||
final newChannel = Channel.fromState(this, channelState);
|
||||
channels[newChannel.cid] = newChannel;
|
||||
channels[newChannel.cid!] = newChannel;
|
||||
newChannels.add(newChannel);
|
||||
}
|
||||
}
|
||||
@@ -1061,16 +1066,13 @@ class StreamChatClient {
|
||||
|
||||
/// A message search.
|
||||
Future<SearchMessagesResponse> search(
|
||||
Map<String, dynamic> filters, {
|
||||
Filter filter, {
|
||||
String? query,
|
||||
List<SortOption>? sort,
|
||||
PaginationParams? paginationParams,
|
||||
Map<String, dynamic>? messageFilters,
|
||||
}) async {
|
||||
assert(() {
|
||||
if (filters.isEmpty) {
|
||||
throw ArgumentError('`filters` cannot be set as null or empty');
|
||||
}
|
||||
if (query == null && messageFilters == null) {
|
||||
throw ArgumentError('Provide at least `query` or `messageFilters`');
|
||||
}
|
||||
@@ -1083,7 +1085,7 @@ class StreamChatClient {
|
||||
}(), 'Check incoming params.');
|
||||
|
||||
final payload = {
|
||||
'filter_conditions': filters,
|
||||
'filter_conditions': filter,
|
||||
'message_filter_conditions': messageFilters,
|
||||
'query': query,
|
||||
'sort': sort,
|
||||
@@ -1194,15 +1196,12 @@ class StreamChatClient {
|
||||
Channel channel(
|
||||
String type, {
|
||||
String? id,
|
||||
Map<String, dynamic> extraData = const {},
|
||||
Map<String, Object> extraData = const {},
|
||||
}) {
|
||||
if (id != null && state.channels?.containsKey('$type:$id') == true) {
|
||||
if (state.channels!['$type:$id'] != null) {
|
||||
return state.channels!['$type:$id'] as Channel;
|
||||
}
|
||||
if (id != null && state.channels.containsKey('$type:$id')) {
|
||||
return state.channels['$type:$id']!;
|
||||
}
|
||||
|
||||
return Channel(this, type, id, extraData);
|
||||
return Channel(this, type, id, extraData: extraData);
|
||||
}
|
||||
|
||||
/// Update or Create the given user object.
|
||||
@@ -1443,9 +1442,7 @@ class ClientState {
|
||||
if (cid != null) {
|
||||
_client.chatPersistenceClient?.deleteChannels([cid]);
|
||||
}
|
||||
if (channels != null) {
|
||||
channels = channels?..removeWhere((cid, ch) => cid == event.cid);
|
||||
}
|
||||
channels = channels..removeWhere((cid, ch) => cid == event.cid);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1468,9 +1465,7 @@ class ClientState {
|
||||
.listen((Event event) async {
|
||||
final eventChannel = event.channel!;
|
||||
await _client.chatPersistenceClient?.deleteChannels([eventChannel.cid]);
|
||||
if (channels != null) {
|
||||
channels = channels?..remove(eventChannel.cid);
|
||||
}
|
||||
channels = channels..remove(eventChannel.cid);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1521,13 +1516,13 @@ class ClientState {
|
||||
_channelsController.stream;
|
||||
|
||||
/// The current list of channels in memory
|
||||
Map<String?, Channel>? get channels => _channelsController.value;
|
||||
Map<String, Channel> get channels => _channelsController.value!;
|
||||
|
||||
set channels(Map<String?, Channel>? v) {
|
||||
_channelsController.add(v);
|
||||
set channels(Map<String, Channel>? v) {
|
||||
if (v != null) _channelsController.add(v);
|
||||
}
|
||||
|
||||
final BehaviorSubject<Map<String?, Channel>?> _channelsController =
|
||||
final BehaviorSubject<Map<String, Channel>> _channelsController =
|
||||
BehaviorSubject.seeded({});
|
||||
final BehaviorSubject<OwnUser?> _userController = BehaviorSubject();
|
||||
final BehaviorSubject<Map<String?, User?>> _usersController =
|
||||
@@ -1541,7 +1536,7 @@ class ClientState {
|
||||
_userController.close();
|
||||
_unreadChannelsController.close();
|
||||
_totalUnreadCountController.close();
|
||||
channels!.values.forEach((c) => c.dispose());
|
||||
channels.values.forEach((c) => c.dispose());
|
||||
_channelsController.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:stream_chat/src/api/requests.dart';
|
||||
import 'package:stream_chat/src/models/channel_model.dart';
|
||||
import 'package:stream_chat/src/models/channel_state.dart';
|
||||
import 'package:stream_chat/src/models/event.dart';
|
||||
import 'package:stream_chat/src/models/filter.dart';
|
||||
import 'package:stream_chat/src/models/member.dart';
|
||||
import 'package:stream_chat/src/models/message.dart';
|
||||
import 'package:stream_chat/src/models/reaction.dart';
|
||||
@@ -90,7 +91,7 @@ abstract class ChatPersistenceClient {
|
||||
/// Optionally, pass [filter], [sort], [paginationParams]
|
||||
/// for filtering out states.
|
||||
Future<List<ChannelState>> getChannelStates({
|
||||
Map<String, dynamic>? filter,
|
||||
Filter? filter,
|
||||
List<SortOption<ChannelModel>>? sort,
|
||||
PaginationParams? paginationParams,
|
||||
});
|
||||
@@ -100,7 +101,7 @@ abstract class ChatPersistenceClient {
|
||||
/// If [clearQueryCache] is true before the insert
|
||||
/// the list of matching rows will be deleted
|
||||
Future<void> updateChannelQueries(
|
||||
Map<String, dynamic> filter,
|
||||
Filter? filter,
|
||||
List<String> cids, {
|
||||
bool clearQueryCache = false,
|
||||
});
|
||||
|
||||
@@ -23,16 +23,16 @@ class ChannelModel {
|
||||
this.memberCount = 0,
|
||||
this.extraData,
|
||||
this.team,
|
||||
}) : config = config ?? ChannelConfig(),
|
||||
createdAt = createdAt ?? DateTime.now(),
|
||||
updatedAt = updatedAt ?? DateTime.now(),
|
||||
assert(
|
||||
}) : assert(
|
||||
(cid != null && cid.contains(':')) || (id != null && type != null),
|
||||
'provide either a cid or an id and type',
|
||||
),
|
||||
id = id ?? cid!.split(':')[1],
|
||||
type = type ?? cid!.split(':')[0],
|
||||
cid = cid ?? '$type:$id';
|
||||
cid = cid ?? '$type:$id',
|
||||
config = config ?? ChannelConfig(),
|
||||
createdAt = createdAt ?? DateTime.now(),
|
||||
updatedAt = updatedAt ?? DateTime.now();
|
||||
|
||||
/// Create a new instance from a json
|
||||
factory ChannelModel.fromJson(Map<String, dynamic> json) =>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// ignore_for_file: non_constant_identifier_names, constant_identifier_names
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
const _groupOperators = [
|
||||
FilterOperator.and,
|
||||
FilterOperator.or,
|
||||
@@ -83,7 +85,7 @@ extension FilterOperatorX on FilterOperator {
|
||||
/// )
|
||||
/// ```
|
||||
/// See <a href="https://getstream.io/chat/docs/query_channels/?language=dart" target="_top">Query Channels Documentation</a>
|
||||
class Filter {
|
||||
class Filter extends Equatable {
|
||||
const Filter.__({
|
||||
required this.operator,
|
||||
required this.value,
|
||||
@@ -171,6 +173,9 @@ class Filter {
|
||||
///
|
||||
final Object /*List<Object>|List<Filter>|String*/ value;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [operator, key, value];
|
||||
|
||||
///
|
||||
Map<String, Object> toJson() {
|
||||
final json = <String, Object>{};
|
||||
|
||||
@@ -516,7 +516,7 @@ void main() {
|
||||
);
|
||||
await channelClient.watch();
|
||||
|
||||
final event = const Event(type: EventType.any);
|
||||
const event = Event(type: EventType.any);
|
||||
|
||||
when(
|
||||
() => mockDio.post<String>(
|
||||
@@ -564,7 +564,7 @@ void main() {
|
||||
);
|
||||
await channelClient.watch();
|
||||
|
||||
final event = const Event(type: EventType.typingStart);
|
||||
const event = Event(type: EventType.typingStart);
|
||||
|
||||
when(
|
||||
() => mockDio.post<String>(
|
||||
@@ -610,7 +610,7 @@ void main() {
|
||||
);
|
||||
await channelClient.watch();
|
||||
|
||||
final event = const Event(type: EventType.typingStop);
|
||||
const event = Event(type: EventType.typingStop);
|
||||
|
||||
when(
|
||||
() => mockDio.post<String>(
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:stream_chat/src/api/requests.dart';
|
||||
import 'package:stream_chat/src/client.dart';
|
||||
import 'package:stream_chat/src/exceptions.dart';
|
||||
import 'package:stream_chat/src/models/channel_model.dart';
|
||||
import 'package:stream_chat/src/models/filter.dart';
|
||||
import 'package:stream_chat/src/models/message.dart';
|
||||
import 'package:stream_chat/src/models/user.dart';
|
||||
import 'package:test/test.dart';
|
||||
@@ -89,7 +90,7 @@ void main() {
|
||||
|
||||
test('Channel', () {
|
||||
final client = StreamChatClient('test');
|
||||
final data = <String, dynamic>{'test': 1};
|
||||
final data = <String, Object>{'test': 1};
|
||||
final channelClient = client.channel('type', id: 'id', extraData: data);
|
||||
expect(channelClient.type, 'type');
|
||||
expect(channelClient.id, 'id');
|
||||
@@ -140,11 +141,7 @@ void main() {
|
||||
when(() => mockDio.interceptors).thenReturn(Interceptors());
|
||||
|
||||
final client = StreamChatClient('api-key', httpClient: mockDio);
|
||||
final queryFilter = <String, dynamic>{
|
||||
'id': {
|
||||
'\$in': ['test'],
|
||||
},
|
||||
};
|
||||
final queryFilter = Filter.in_('id', ['test']);
|
||||
final sortOptions = <SortOption<ChannelModel>>[];
|
||||
final options = {'state': false, 'watch': false, 'presence': true};
|
||||
const paginationParams = PaginationParams(offset: 2);
|
||||
@@ -192,11 +189,7 @@ void main() {
|
||||
when(() => mockDio.interceptors).thenReturn(Interceptors());
|
||||
|
||||
final client = StreamChatClient('api-key', httpClient: mockDio);
|
||||
final filter = {
|
||||
'cid': {
|
||||
r'$in': ['messaging:testId']
|
||||
}
|
||||
};
|
||||
final filter = Filter.in_('cid', ['messaging:testId']);
|
||||
const query = 'hello';
|
||||
final queryParams = {
|
||||
'payload': json.encode({
|
||||
@@ -229,11 +222,7 @@ void main() {
|
||||
when(() => mockDio.interceptors).thenReturn(Interceptors());
|
||||
|
||||
final client = StreamChatClient('api-key', httpClient: mockDio);
|
||||
final filters = {
|
||||
'id': {
|
||||
'\$in': ['test'],
|
||||
},
|
||||
};
|
||||
final filters = Filter.in_('id', ['test']);
|
||||
const sortOptions = [SortOption('name')];
|
||||
const query = 'query';
|
||||
final queryParams = {
|
||||
@@ -1163,8 +1152,11 @@ void main() {
|
||||
httpClient: mockDio,
|
||||
);
|
||||
|
||||
final channelClient =
|
||||
client.channel('type', id: 'id', extraData: {'name': 'init'});
|
||||
final channelClient = client.channel(
|
||||
'type',
|
||||
id: 'id',
|
||||
extraData: {'name': 'init'},
|
||||
);
|
||||
|
||||
const update = {
|
||||
'set': {'name': 'demo'}
|
||||
|
||||
Reference in New Issue
Block a user