@@ -22,9 +22,9 @@ class Channel {
|
|||||||
this._client,
|
this._client,
|
||||||
this._type,
|
this._type,
|
||||||
this._id, {
|
this._id, {
|
||||||
Map<String, Object?> extraData = const {},
|
Map<String, Object?>? extraData,
|
||||||
}) : _cid = _id != null ? '$_type:$_id' : null,
|
}) : _cid = _id != null ? '$_type:$_id' : null,
|
||||||
_extraData = extraData {
|
_extraData = extraData ?? {} {
|
||||||
_client.logger.info('New Channel instance not initialized created');
|
_client.logger.info('New Channel instance not initialized created');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,8 +202,13 @@ class Channel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Channel extra data
|
/// Channel extra data
|
||||||
Map<String, dynamic> get extraData =>
|
Map<String, Object?> get extraData {
|
||||||
state?._channelState.channel?.extraData ?? _extraData;
|
var data = state?._channelState.channel?.extraData;
|
||||||
|
if (data == null || data.isEmpty) {
|
||||||
|
data = _extraData;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
/// Channel extra data as a stream
|
/// Channel extra data as a stream
|
||||||
Stream<Map<String, dynamic>> get extraDataStream {
|
Stream<Map<String, dynamic>> get extraDataStream {
|
||||||
@@ -942,7 +947,7 @@ class Channel {
|
|||||||
state = ChannelClientState(this, channelState);
|
state = ChannelClientState(this, channelState);
|
||||||
|
|
||||||
if (cid != null) {
|
if (cid != null) {
|
||||||
client.state.channels[cid!] = this;
|
client.state.channels = {cid!: this};
|
||||||
}
|
}
|
||||||
if (!_initializedCompleter.isCompleted) {
|
if (!_initializedCompleter.isCompleted) {
|
||||||
_initializedCompleter.complete(true);
|
_initializedCompleter.complete(true);
|
||||||
@@ -959,8 +964,8 @@ class Channel {
|
|||||||
/// Set [preferOffline] to true to avoid the api call if the data is already
|
/// Set [preferOffline] to true to avoid the api call if the data is already
|
||||||
/// in the offline storage
|
/// in the offline storage
|
||||||
Future<QueryRepliesResponse> getReplies(
|
Future<QueryRepliesResponse> getReplies(
|
||||||
String parentId,
|
String parentId, {
|
||||||
PaginationParams options, {
|
PaginationParams? options,
|
||||||
bool preferOffline = false,
|
bool preferOffline = false,
|
||||||
}) async {
|
}) async {
|
||||||
final cachedReplies = await _client.chatPersistenceClient?.getReplies(
|
final cachedReplies = await _client.chatPersistenceClient?.getReplies(
|
||||||
@@ -973,19 +978,22 @@ class Channel {
|
|||||||
return QueryRepliesResponse()..messages = cachedReplies;
|
return QueryRepliesResponse()..messages = cachedReplies;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final repliesResponse = await _client.getReplies(parentId, options);
|
final repliesResponse = await _client.getReplies(
|
||||||
|
parentId,
|
||||||
|
options: options,
|
||||||
|
);
|
||||||
state?.updateThreadInfo(parentId, repliesResponse.messages);
|
state?.updateThreadInfo(parentId, repliesResponse.messages);
|
||||||
return repliesResponse;
|
return repliesResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List the reactions for a message in the channel
|
/// List the reactions for a message in the channel
|
||||||
Future<QueryReactionsResponse> getReactions(
|
Future<QueryReactionsResponse> getReactions(
|
||||||
String messageId,
|
String messageId, {
|
||||||
PaginationParams options,
|
PaginationParams? options,
|
||||||
) =>
|
}) =>
|
||||||
_client.getReactions(
|
_client.getReactions(
|
||||||
messageId,
|
messageId,
|
||||||
options,
|
options: options,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Retrieves a list of messages by ID
|
/// Retrieves a list of messages by ID
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class StreamChatClient {
|
|||||||
/// application.
|
/// application.
|
||||||
StreamChatClient(
|
StreamChatClient(
|
||||||
String apiKey, {
|
String apiKey, {
|
||||||
this.logLevel = Level.ALL,
|
this.logLevel = Level.WARNING,
|
||||||
LogHandlerFunction? logHandlerFunction,
|
LogHandlerFunction? logHandlerFunction,
|
||||||
RetryPolicy? retryPolicy,
|
RetryPolicy? retryPolicy,
|
||||||
Location? location,
|
Location? location,
|
||||||
@@ -71,9 +71,11 @@ class StreamChatClient {
|
|||||||
Duration connectTimeout = const Duration(seconds: 6),
|
Duration connectTimeout = const Duration(seconds: 6),
|
||||||
Duration receiveTimeout = const Duration(seconds: 6),
|
Duration receiveTimeout = const Duration(seconds: 6),
|
||||||
StreamChatApi? chatApi,
|
StreamChatApi? chatApi,
|
||||||
|
WebSocket? ws,
|
||||||
AttachmentFileUploader? attachmentFileUploader,
|
AttachmentFileUploader? attachmentFileUploader,
|
||||||
}) {
|
}) {
|
||||||
_setupLogger(logHandlerFunction);
|
this.logHandlerFunction = logHandlerFunction ?? _defaultLogHandler;
|
||||||
|
logger.info('Initiating new StreamChatClient');
|
||||||
|
|
||||||
final options = StreamHttpClientOptions(
|
final options = StreamHttpClientOptions(
|
||||||
baseUrl: baseURL,
|
baseUrl: baseURL,
|
||||||
@@ -92,13 +94,14 @@ class StreamChatClient {
|
|||||||
logger: detachedLogger('🕸️'),
|
logger: detachedLogger('🕸️'),
|
||||||
);
|
);
|
||||||
|
|
||||||
_ws = WebSocket(
|
_ws = ws ??
|
||||||
apiKey: apiKey,
|
WebSocket(
|
||||||
baseUrl: options.baseUrl,
|
apiKey: apiKey,
|
||||||
tokenManager: _tokenManager,
|
baseUrl: options.baseUrl,
|
||||||
handler: handleEvent,
|
tokenManager: _tokenManager,
|
||||||
logger: detachedLogger('🔌'),
|
handler: handleEvent,
|
||||||
);
|
logger: detachedLogger('🔌'),
|
||||||
|
);
|
||||||
|
|
||||||
_retryPolicy = retryPolicy ??
|
_retryPolicy = retryPolicy ??
|
||||||
RetryPolicy(
|
RetryPolicy(
|
||||||
@@ -107,8 +110,6 @@ class StreamChatClient {
|
|||||||
);
|
);
|
||||||
|
|
||||||
state = ClientState(this);
|
state = ClientState(this);
|
||||||
|
|
||||||
logger.info('instantiating new client');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
late final StreamChatApi _chatApi;
|
late final StreamChatApi _chatApi;
|
||||||
@@ -160,7 +161,7 @@ class StreamChatClient {
|
|||||||
/// Client specific logger instance.
|
/// Client specific logger instance.
|
||||||
/// Refer to the class [Logger] to learn more about the specific
|
/// Refer to the class [Logger] to learn more about the specific
|
||||||
/// implementation.
|
/// implementation.
|
||||||
final Logger logger = Logger.detached('📡');
|
late final Logger logger = detachedLogger('📡');
|
||||||
|
|
||||||
/// A function that has a parameter of type [LogRecord].
|
/// A function that has a parameter of type [LogRecord].
|
||||||
/// This is called on every new log record.
|
/// This is called on every new log record.
|
||||||
@@ -185,7 +186,7 @@ class StreamChatClient {
|
|||||||
|
|
||||||
final _eventController = BehaviorSubject<Event>();
|
final _eventController = BehaviorSubject<Event>();
|
||||||
|
|
||||||
/// Stream of [Event] coming from websocket connection
|
/// Stream of [Event] coming from [_ws] connection
|
||||||
/// Listen to this or use the [on] method to filter specific event types
|
/// Listen to this or use the [on] method to filter specific event types
|
||||||
Stream<Event> get eventStream => _eventController.stream;
|
Stream<Event> get eventStream => _eventController.stream;
|
||||||
|
|
||||||
@@ -195,12 +196,12 @@ class StreamChatClient {
|
|||||||
set _wsConnectionStatus(ConnectionStatus status) =>
|
set _wsConnectionStatus(ConnectionStatus status) =>
|
||||||
_wsConnectionStatusController.add(status);
|
_wsConnectionStatusController.add(status);
|
||||||
|
|
||||||
/// The current status value of the websocket connection
|
/// The current status value of the [_ws] connection
|
||||||
ConnectionStatus get wsConnectionStatus =>
|
ConnectionStatus get wsConnectionStatus =>
|
||||||
_wsConnectionStatusController.value;
|
_wsConnectionStatusController.value;
|
||||||
|
|
||||||
/// This notifies the connection status of the websocket connection.
|
/// This notifies the connection status of the [_ws] connection.
|
||||||
/// Listen to this to get notified when the websocket tries to reconnect.
|
/// Listen to this to get notified when the [_ws] tries to reconnect.
|
||||||
Stream<ConnectionStatus> get wsConnectionStatusStream =>
|
Stream<ConnectionStatus> get wsConnectionStatusStream =>
|
||||||
_wsConnectionStatusController.stream.distinct();
|
_wsConnectionStatusController.stream.distinct();
|
||||||
|
|
||||||
@@ -215,19 +216,9 @@ class StreamChatClient {
|
|||||||
};
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
Logger detachedLogger(
|
Logger detachedLogger(String name) => Logger.detached(name)
|
||||||
String name,
|
..level = logLevel
|
||||||
) =>
|
..onRecord.listen(logHandlerFunction);
|
||||||
Logger.detached(name)
|
|
||||||
..level = logLevel
|
|
||||||
..onRecord.listen(logHandlerFunction);
|
|
||||||
|
|
||||||
void _setupLogger(LogHandlerFunction? logHandlerFunction) {
|
|
||||||
logger.level = logLevel;
|
|
||||||
this.logHandlerFunction = logHandlerFunction ?? _defaultLogHandler;
|
|
||||||
logger.onRecord.listen(this.logHandlerFunction);
|
|
||||||
logger.info('logger setup');
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Connects the current user, this triggers a connection to the API.
|
/// Connects the current user, this triggers a connection to the API.
|
||||||
/// It returns a [Future] that resolves when the connection is setup.
|
/// It returns a [Future] that resolves when the connection is setup.
|
||||||
@@ -325,9 +316,13 @@ class StreamChatClient {
|
|||||||
|
|
||||||
_wsConnectionStatus = ConnectionStatus.connecting;
|
_wsConnectionStatus = ConnectionStatus.connecting;
|
||||||
|
|
||||||
_connectionStatusSubscription = _ws.connectionStatusStream.listen(
|
// skipping `ws` seed connection status -> ConnectionStatus.disconnected
|
||||||
_connectionStatusHandler,
|
// otherwise `client.wsConnectionStatusStream` will emit in order
|
||||||
);
|
// 1. ConnectionStatus.disconnected -> client seed status
|
||||||
|
// 2. ConnectionStatus.connecting -> client connecting status
|
||||||
|
// 3. ConnectionStatus.disconnected -> ws seed status
|
||||||
|
_connectionStatusSubscription =
|
||||||
|
_ws.connectionStatusStream.skip(1).listen(_connectionStatusHandler);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await _ws.connect(user);
|
return await _ws.connect(user);
|
||||||
@@ -768,13 +763,13 @@ class StreamChatClient {
|
|||||||
_chatApi.device.removeDevice(id);
|
_chatApi.device.removeDevice(id);
|
||||||
|
|
||||||
/// Get a development token
|
/// Get a development token
|
||||||
String devToken(String userId) => Token.development(userId).rawValue;
|
Token devToken(String userId) => Token.development(userId);
|
||||||
|
|
||||||
/// Returns a channel client with the given type, id and custom data.
|
/// Returns a channel client with the given type, id and custom data.
|
||||||
Channel channel(
|
Channel channel(
|
||||||
String type, {
|
String type, {
|
||||||
String? id,
|
String? id,
|
||||||
Map<String, Object?> extraData = const {},
|
Map<String, Object?>? extraData,
|
||||||
}) {
|
}) {
|
||||||
if (id != null && state.channels.containsKey('$type:$id')) {
|
if (id != null && state.channels.containsKey('$type:$id')) {
|
||||||
return state.channels['$type:$id']!;
|
return state.channels['$type:$id']!;
|
||||||
@@ -796,6 +791,7 @@ class StreamChatClient {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// watches the provided channel
|
/// watches the provided channel
|
||||||
|
/// Creates first if not yet created
|
||||||
Future<ChannelState> watchChannel(
|
Future<ChannelState> watchChannel(
|
||||||
String channelType, {
|
String channelType, {
|
||||||
String? channelId,
|
String? channelId,
|
||||||
@@ -809,6 +805,7 @@ class StreamChatClient {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// Query the API, get messages, members or other channel fields
|
/// Query the API, get messages, members or other channel fields
|
||||||
|
/// Creates the channel first if not yet created
|
||||||
Future<ChannelState> queryChannel(
|
Future<ChannelState> queryChannel(
|
||||||
String channelType, {
|
String channelType, {
|
||||||
bool state = true,
|
bool state = true,
|
||||||
@@ -1139,22 +1136,22 @@ class StreamChatClient {
|
|||||||
|
|
||||||
/// Lists all the message replies for the [parentId]
|
/// Lists all the message replies for the [parentId]
|
||||||
Future<QueryRepliesResponse> getReplies(
|
Future<QueryRepliesResponse> getReplies(
|
||||||
String parentId,
|
String parentId, {
|
||||||
PaginationParams options,
|
PaginationParams? options,
|
||||||
) =>
|
}) =>
|
||||||
_chatApi.message.getReplies(
|
_chatApi.message.getReplies(
|
||||||
parentId,
|
parentId,
|
||||||
options,
|
options: options,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Get all the reactions for a [messageId]
|
/// Get all the reactions for a [messageId]
|
||||||
Future<QueryReactionsResponse> getReactions(
|
Future<QueryReactionsResponse> getReactions(
|
||||||
String messageId,
|
String messageId, {
|
||||||
PaginationParams options,
|
PaginationParams? options,
|
||||||
) =>
|
}) =>
|
||||||
_chatApi.message.getReactions(
|
_chatApi.message.getReactions(
|
||||||
messageId,
|
messageId,
|
||||||
options,
|
options: options,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Update the given message
|
/// Update the given message
|
||||||
|
|||||||
@@ -142,13 +142,13 @@ class MessageApi {
|
|||||||
|
|
||||||
/// Get all the reactions for a [messageId]
|
/// Get all the reactions for a [messageId]
|
||||||
Future<QueryReactionsResponse> getReactions(
|
Future<QueryReactionsResponse> getReactions(
|
||||||
String messageId,
|
String messageId, {
|
||||||
PaginationParams options,
|
PaginationParams? options,
|
||||||
) async {
|
}) async {
|
||||||
final response = await _client.get(
|
final response = await _client.get(
|
||||||
'/messages/$messageId/reactions',
|
'/messages/$messageId/reactions',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
...options.toJson(),
|
if (options != null) ...options.toJson(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return QueryReactionsResponse.fromJson(response.data);
|
return QueryReactionsResponse.fromJson(response.data);
|
||||||
@@ -168,13 +168,13 @@ class MessageApi {
|
|||||||
|
|
||||||
/// Lists all the message replies for the [parentId]
|
/// Lists all the message replies for the [parentId]
|
||||||
Future<QueryRepliesResponse> getReplies(
|
Future<QueryRepliesResponse> getReplies(
|
||||||
String parentId,
|
String parentId, {
|
||||||
PaginationParams options,
|
PaginationParams? options,
|
||||||
) async {
|
}) async {
|
||||||
final response = await _client.get(
|
final response = await _client.get(
|
||||||
'/messages/$parentId/replies',
|
'/messages/$parentId/replies',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
...options.toJson(),
|
if (options != null) ...options.toJson(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return QueryRepliesResponse.fromJson(response.data);
|
return QueryRepliesResponse.fromJson(response.data);
|
||||||
|
|||||||
@@ -1490,7 +1490,6 @@ void main() {
|
|||||||
|
|
||||||
test('`.getReplies`', () async {
|
test('`.getReplies`', () async {
|
||||||
const parentId = 'test-parent-id';
|
const parentId = 'test-parent-id';
|
||||||
const options = PaginationParams();
|
|
||||||
|
|
||||||
final messages = List.generate(
|
final messages = List.generate(
|
||||||
3,
|
3,
|
||||||
@@ -1500,22 +1499,21 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
when(() => client.getReplies(parentId, options)).thenAnswer(
|
when(() => client.getReplies(parentId)).thenAnswer(
|
||||||
(_) async => QueryRepliesResponse()..messages = messages,
|
(_) async => QueryRepliesResponse()..messages = messages,
|
||||||
);
|
);
|
||||||
|
|
||||||
final res = await channel.getReplies(parentId, options);
|
final res = await channel.getReplies(parentId);
|
||||||
|
|
||||||
expect(res, isNotNull);
|
expect(res, isNotNull);
|
||||||
expect(res.messages.length, messages.length);
|
expect(res.messages.length, messages.length);
|
||||||
expect(res.messages.every((it) => it.parentId == parentId), isTrue);
|
expect(res.messages.every((it) => it.parentId == parentId), isTrue);
|
||||||
|
|
||||||
verify(() => client.getReplies(parentId, options)).called(1);
|
verify(() => client.getReplies(parentId)).called(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('`.getReactions`', () async {
|
test('`.getReactions`', () async {
|
||||||
const messageId = 'test-message-id';
|
const messageId = 'test-message-id';
|
||||||
const options = PaginationParams();
|
|
||||||
|
|
||||||
final reactions = List.generate(
|
final reactions = List.generate(
|
||||||
3,
|
3,
|
||||||
@@ -1525,17 +1523,17 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
when(() => client.getReactions(messageId, options)).thenAnswer(
|
when(() => client.getReactions(messageId)).thenAnswer(
|
||||||
(_) async => QueryReactionsResponse()..reactions = reactions,
|
(_) async => QueryReactionsResponse()..reactions = reactions,
|
||||||
);
|
);
|
||||||
|
|
||||||
final res = await channel.getReactions(messageId, options);
|
final res = await channel.getReactions(messageId);
|
||||||
|
|
||||||
expect(res, isNotNull);
|
expect(res, isNotNull);
|
||||||
expect(res.reactions.length, reactions.length);
|
expect(res.reactions.length, reactions.length);
|
||||||
expect(res.reactions.every((it) => it.messageId == messageId), isTrue);
|
expect(res.reactions.every((it) => it.messageId == messageId), isTrue);
|
||||||
|
|
||||||
verify(() => client.getReactions(messageId, options)).called(1);
|
verify(() => client.getReactions(messageId)).called(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('`.getMessagesById`', () async {
|
test('`.getMessagesById`', () async {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -252,7 +252,7 @@ void main() {
|
|||||||
'reactions': [...reactions.map((it) => it.toJson())]
|
'reactions': [...reactions.map((it) => it.toJson())]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
final res = await messageApi.getReactions(messageId, options);
|
final res = await messageApi.getReactions(messageId, options: options);
|
||||||
|
|
||||||
expect(res, isNotNull);
|
expect(res, isNotNull);
|
||||||
expect(res.reactions.length, reactions.length);
|
expect(res.reactions.length, reactions.length);
|
||||||
@@ -311,7 +311,7 @@ void main() {
|
|||||||
'messages': [...messages.map((it) => it.toJson())]
|
'messages': [...messages.map((it) => it.toJson())]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
final res = await messageApi.getReplies(parentId, options);
|
final res = await messageApi.getReplies(parentId, options: options);
|
||||||
|
|
||||||
expect(res, isNotNull);
|
expect(res, isNotNull);
|
||||||
expect(res.messages.length, messages.length);
|
expect(res.messages.length, messages.length);
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:mocktail/mocktail.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:stream_chat/src/core/api/channel_api.dart';
|
import 'package:stream_chat/src/core/api/channel_api.dart';
|
||||||
import 'package:stream_chat/src/core/api/device_api.dart';
|
import 'package:stream_chat/src/core/api/device_api.dart';
|
||||||
import 'package:stream_chat/src/core/api/general_api.dart';
|
import 'package:stream_chat/src/core/api/general_api.dart';
|
||||||
@@ -10,6 +13,7 @@ import 'package:stream_chat/src/core/api/user_api.dart';
|
|||||||
import 'package:stream_chat/src/core/api/guest_api.dart';
|
import 'package:stream_chat/src/core/api/guest_api.dart';
|
||||||
import 'package:stream_chat/src/core/http/token.dart';
|
import 'package:stream_chat/src/core/http/token.dart';
|
||||||
import 'package:stream_chat/src/core/http/token_manager.dart';
|
import 'package:stream_chat/src/core/http/token_manager.dart';
|
||||||
|
import 'package:stream_chat/src/ws/websocket.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -96,3 +100,88 @@ class FakeMessage extends Fake implements Message {}
|
|||||||
class FakeAttachmentFile extends Fake implements AttachmentFile {}
|
class FakeAttachmentFile extends Fake implements AttachmentFile {}
|
||||||
|
|
||||||
class FakeEvent extends Fake implements Event {}
|
class FakeEvent extends Fake implements Event {}
|
||||||
|
|
||||||
|
class FakeUser extends Fake implements User {}
|
||||||
|
|
||||||
|
class FakeWebSocket extends Fake implements WebSocket {
|
||||||
|
BehaviorSubject<ConnectionStatus>? _connectionStatusController;
|
||||||
|
|
||||||
|
BehaviorSubject<ConnectionStatus> get connectionStatusController =>
|
||||||
|
_connectionStatusController ??=
|
||||||
|
BehaviorSubject.seeded(ConnectionStatus.disconnected);
|
||||||
|
|
||||||
|
set connectionStatus(ConnectionStatus value) {
|
||||||
|
connectionStatusController.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConnectionStatus get connectionStatus => connectionStatusController.value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<ConnectionStatus> get connectionStatusStream =>
|
||||||
|
connectionStatusController.stream;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Completer<Event>? connectionCompleter;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Event> connect(User user) async {
|
||||||
|
connectionStatus = ConnectionStatus.connecting;
|
||||||
|
final event = Event(
|
||||||
|
type: EventType.healthCheck,
|
||||||
|
connectionId: 'fake-connection-id',
|
||||||
|
me: OwnUser.fromUser(user),
|
||||||
|
);
|
||||||
|
connectionCompleter = Completer()..complete(event);
|
||||||
|
connectionStatus = ConnectionStatus.connected;
|
||||||
|
return connectionCompleter!.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void disconnect() {
|
||||||
|
connectionStatus = ConnectionStatus.disconnected;
|
||||||
|
connectionCompleter = null;
|
||||||
|
_connectionStatusController?.close();
|
||||||
|
_connectionStatusController = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeWebSocketWithConnectionError extends Fake implements WebSocket {
|
||||||
|
BehaviorSubject<ConnectionStatus>? _connectionStatusController;
|
||||||
|
|
||||||
|
BehaviorSubject<ConnectionStatus> get connectionStatusController =>
|
||||||
|
_connectionStatusController ??=
|
||||||
|
BehaviorSubject.seeded(ConnectionStatus.disconnected);
|
||||||
|
|
||||||
|
set connectionStatus(ConnectionStatus value) {
|
||||||
|
connectionStatusController.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConnectionStatus get connectionStatus => connectionStatusController.value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<ConnectionStatus> get connectionStatusStream =>
|
||||||
|
connectionStatusController.stream;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Completer<Event>? connectionCompleter;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Event> connect(User user) async {
|
||||||
|
connectionStatus = ConnectionStatus.connecting;
|
||||||
|
const error = StreamWebSocketError('Error Connecting');
|
||||||
|
connectionCompleter = Completer()..completeError(error);
|
||||||
|
return connectionCompleter!.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void disconnect() {
|
||||||
|
connectionStatus = ConnectionStatus.disconnected;
|
||||||
|
connectionCompleter = null;
|
||||||
|
_connectionStatusController?.close();
|
||||||
|
_connectionStatusController = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeChannelState extends Fake implements ChannelState {}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:dio/dio.dart' show MultipartFile;
|
import 'package:dio/dio.dart' show MultipartFile;
|
||||||
|
import 'package:stream_chat/src/client/channel.dart';
|
||||||
|
import 'package:stream_chat/src/core/models/channel_model.dart';
|
||||||
|
import 'package:stream_chat/src/core/models/channel_state.dart';
|
||||||
import 'package:stream_chat/src/core/models/event.dart';
|
import 'package:stream_chat/src/core/models/event.dart';
|
||||||
import 'package:stream_chat/src/core/models/message.dart';
|
import 'package:stream_chat/src/core/models/message.dart';
|
||||||
|
import 'package:stream_chat/src/core/models/user.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
Matcher isSameMultipartFileAs(MultipartFile targetFile) =>
|
Matcher isSameMultipartFileAs(MultipartFile targetFile) =>
|
||||||
@@ -96,3 +100,35 @@ class _IsSameMessageAs extends Matcher {
|
|||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Matcher isSameUserAs(User targetUser) => _IsSameUserAs(targetUser: targetUser);
|
||||||
|
|
||||||
|
class _IsSameUserAs extends Matcher {
|
||||||
|
const _IsSameUserAs({required this.targetUser});
|
||||||
|
|
||||||
|
final User targetUser;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Description describe(Description description) =>
|
||||||
|
description.add('is same user as $targetUser');
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool matches(covariant User user, Map matchState) => user.id == targetUser.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher isCorrectChannelFor(ChannelState channelState) =>
|
||||||
|
_IsCorrectChannelFor(channelState: channelState);
|
||||||
|
|
||||||
|
class _IsCorrectChannelFor extends Matcher {
|
||||||
|
const _IsCorrectChannelFor({required this.channelState});
|
||||||
|
|
||||||
|
final ChannelState channelState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Description describe(Description description) =>
|
||||||
|
description.add('is correct channel for $channelState');
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool matches(covariant Channel channel, Map matchState) =>
|
||||||
|
channel.cid == channelState.channel?.cid;
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import 'package:stream_chat/src/core/http/token_manager.dart';
|
|||||||
import 'package:stream_chat/src/core/models/channel_config.dart';
|
import 'package:stream_chat/src/core/models/channel_config.dart';
|
||||||
import 'package:stream_chat/src/core/models/channel_model.dart';
|
import 'package:stream_chat/src/core/models/channel_model.dart';
|
||||||
import 'package:stream_chat/src/db/chat_persistence_client.dart';
|
import 'package:stream_chat/src/db/chat_persistence_client.dart';
|
||||||
|
import 'package:stream_chat/src/ws/websocket.dart';
|
||||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||||
|
|
||||||
import 'db/chat_persistence_client_test.dart';
|
import 'db/chat_persistence_client_test.dart';
|
||||||
@@ -66,7 +67,13 @@ class MockGeneralApi extends Mock implements GeneralApi {}
|
|||||||
class MockAttachmentFileUploader extends Mock
|
class MockAttachmentFileUploader extends Mock
|
||||||
implements AttachmentFileUploader {}
|
implements AttachmentFileUploader {}
|
||||||
|
|
||||||
class MockPersistenceClient extends Mock implements ChatPersistenceClient {}
|
class MockPersistenceClient extends Mock implements ChatPersistenceClient {
|
||||||
|
@override
|
||||||
|
Future<void> connect(String userId) => Future.value();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> disconnect({bool flush = false}) => Future.value();
|
||||||
|
}
|
||||||
|
|
||||||
class MockStreamChatClient extends Mock implements StreamChatClient {
|
class MockStreamChatClient extends Mock implements StreamChatClient {
|
||||||
@override
|
@override
|
||||||
@@ -105,3 +112,5 @@ class MockRetryQueueChannel extends Mock implements Channel {
|
|||||||
@override
|
@override
|
||||||
StreamChatClient get client => _client ??= MockStreamChatClient();
|
StreamChatClient get client => _client ??= MockStreamChatClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MockWebSocket extends Mock implements WebSocket {}
|
||||||
|
|||||||
@@ -13,3 +13,12 @@ Directory get currentDirectory {
|
|||||||
}
|
}
|
||||||
return directory;
|
return directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extension function to convert int into durations
|
||||||
|
extension IntX on num {
|
||||||
|
Duration toDuration() => Duration(milliseconds: toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top level util function to delay the code execution
|
||||||
|
Future delay(num milliseconds) =>
|
||||||
|
Future.delayed(Duration(milliseconds: milliseconds.toInt()));
|
||||||
|
|||||||
@@ -791,7 +791,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
final channel = streamChannel.channel;
|
final channel = streamChannel.channel;
|
||||||
if (_upToDate &&
|
if (_upToDate &&
|
||||||
channel.config?.readEvents == true &&
|
channel.config?.readEvents == true &&
|
||||||
channel.state!.unreadCount! > 0) {
|
channel.state!.unreadCount > 0) {
|
||||||
streamChannel.channel.markRead();
|
streamChannel.channel.markRead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ class StreamChannelState extends State<StreamChannel> {
|
|||||||
try {
|
try {
|
||||||
final response = await channel.getReplies(
|
final response = await channel.getReplies(
|
||||||
parentId,
|
parentId,
|
||||||
PaginationParams(
|
options: PaginationParams(
|
||||||
lessThan: message?.id,
|
lessThan: message?.id,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user