diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index b001d99e..68a55728 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,3 +1,9 @@ +## Upcoming + +🔄 Changed + +- Updated `dio` dependency to `^5.2.0`. + ## 6.2.0 🐞 Fixed @@ -8,6 +14,9 @@ ✅ Added - Added support for `ChatPersistenceClient.isConnected` for checking if the client is connected to the database. +- Added support for `ChatPersistenceClient.userId` for getting the current connected user id. +- Added two new methods `ChatPersistenceClient.disconnect` and `ChatPersistenceClient.connect` for disconnecting and + connecting to the database. ## 6.1.0 diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index 3b249fca..54feb2ab 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -1379,13 +1379,14 @@ class Channel { this.state?.updateChannelState(updatedState); return updatedState; } catch (e) { - if (!_client.persistenceEnabled) { - rethrow; + if (_client.persistenceEnabled) { + return _client.chatPersistenceClient!.getChannelStateByCid( + cid!, + messagePagination: messagesPagination, + ); } - return _client.chatPersistenceClient!.getChannelStateByCid( - cid!, - messagePagination: messagesPagination, - ); + + rethrow; } } @@ -1841,9 +1842,7 @@ class ChannelClientState { /// [isUpToDate] flag count as a stream. Stream get isUpToDateStream => _isUpToDateController.stream; - - final BehaviorSubject _isUpToDateController = - BehaviorSubject.seeded(true); + final _isUpToDateController = BehaviorSubject.seeded(true); /// The retry queue associated to this channel. late final RetryQueue _retryQueue; diff --git a/packages/stream_chat/lib/src/client/client.dart b/packages/stream_chat/lib/src/client/client.dart index 7627cf1d..54fefd57 100644 --- a/packages/stream_chat/lib/src/client/client.dart +++ b/packages/stream_chat/lib/src/client/client.dart @@ -125,10 +125,6 @@ class StreamChatClient { final _tokenManager = TokenManager(); final _connectionIdManager = ConnectionIdManager(); - set chatPersistenceClient(ChatPersistenceClient? value) { - _originalChatPersistenceClient = value; - } - /// Default user agent for all requests static String defaultUserAgent = 'stream-chat-dart-client-${CurrentPlatform.name}'; @@ -139,15 +135,15 @@ class StreamChatClient { /// The current package version static const packageVersion = PACKAGE_VERSION; - ChatPersistenceClient? _originalChatPersistenceClient; - /// Chat persistence client - ChatPersistenceClient? get chatPersistenceClient => _chatPersistenceClient; + ChatPersistenceClient? chatPersistenceClient; - ChatPersistenceClient? _chatPersistenceClient; - - /// Whether the chat persistence is available or not - bool get persistenceEnabled => _chatPersistenceClient != null; + /// Returns `True` if the [chatPersistenceClient] is available and connected. + /// Otherwise, returns `False`. + bool get persistenceEnabled { + final client = chatPersistenceClient; + return client != null && client.isConnected; + } late final RetryPolicy _retryPolicy; @@ -324,20 +320,27 @@ class StreamChatClient { final ownUser = OwnUser.fromUser(user); state.currentUser = ownUser; - if (!connectWebSocket) return ownUser; - try { - if (_originalChatPersistenceClient != null) { - _chatPersistenceClient = _originalChatPersistenceClient; - await _chatPersistenceClient!.connect(ownUser.id); + // Connect to persistence client if its set. + if (chatPersistenceClient != null) { + await openPersistenceConnection(ownUser); } - final connectedUser = await openConnection( - includeUserDetailsInConnectCall: true, - ); - return state.currentUser = connectedUser; + + // Connect to websocket if [connectWebSocket] is true. + // + // This is useful when you want to connect to websocket + // at a later stage or use the client in connection-less mode. + if (connectWebSocket) { + final connectedUser = await openConnection( + includeUserDetailsInConnectCall: true, + ); + state.currentUser = connectedUser; + } + + return state.currentUser!; } catch (e, stk) { if (e is StreamWebSocketError && e.isRetriable) { - final event = await _chatPersistenceClient?.getConnectionInfo(); + final event = await chatPersistenceClient?.getConnectionInfo(); if (event != null) return ownUser.merge(event.me); } logger.severe('error connecting user : ${ownUser.id}', e, stk); @@ -345,6 +348,40 @@ class StreamChatClient { } } + /// Connects the [chatPersistenceClient] to the given [user]. + Future openPersistenceConnection(User user) async { + final client = chatPersistenceClient; + if (client == null) { + throw const StreamChatError('Chat persistence client is not set'); + } + + if (client.isConnected) { + // If the persistence client is already connected to the userId, + // we don't need to connect again. + if (client.userId == user.id) return; + + throw const StreamChatError(''' + Chat persistence client is already connected to a different user, + please close the connection before connecting a new one.'''); + } + + // Connect the persistence client to the userId. + return client.connect(user.id); + } + + /// Disconnects the [chatPersistenceClient] from the current user. + Future closePersistenceConnection({bool flush = false}) async { + final client = chatPersistenceClient; + // If the persistence client is never connected, we don't need to close it. + if (client == null || !client.isConnected) { + logger.info('Chat persistence client is not connected'); + return; + } + + // Disconnect the persistence client. + return client.disconnect(flush: flush); + } + /// Creates a new WebSocket connection with the current user. /// If [includeUserDetailsInConnectCall] is true it will include the current /// user details in the connect call. @@ -422,7 +459,7 @@ class StreamChatClient { final connectionId = event.connectionId; if (connectionId != null) { _connectionIdManager.setConnectionId(connectionId); - _chatPersistenceClient?.updateConnectionInfo(event); + chatPersistenceClient?.updateConnectionInfo(event); } } @@ -460,9 +497,9 @@ class StreamChatClient { // channels are empty, assuming it's a fresh start // and making sure `lastSyncAt` is initialized if (persistenceEnabled) { - final lastSyncAt = await _chatPersistenceClient?.getLastSyncAt(); + final lastSyncAt = await chatPersistenceClient?.getLastSyncAt(); if (lastSyncAt == null) { - await _chatPersistenceClient?.updateLastSyncAt(DateTime.now()); + await chatPersistenceClient?.updateLastSyncAt(DateTime.now()); } } } @@ -493,13 +530,12 @@ class StreamChatClient { /// Will automatically fetch [cids] and [lastSyncedAt] if [persistenceEnabled] Future sync({List? cids, DateTime? lastSyncAt}) { return synchronized(() async { - final channels = cids ?? await _chatPersistenceClient?.getChannelCids(); + final channels = cids ?? await chatPersistenceClient?.getChannelCids(); if (channels == null || channels.isEmpty) { return; } - final syncAt = - lastSyncAt ?? await _chatPersistenceClient?.getLastSyncAt(); + final syncAt = lastSyncAt ?? await chatPersistenceClient?.getLastSyncAt(); if (syncAt == null) { return; } @@ -520,7 +556,7 @@ class StreamChatClient { final now = DateTime.now(); _lastSyncedAt = now; - _chatPersistenceClient?.updateLastSyncAt(now); + chatPersistenceClient?.updateLastSyncAt(now); } catch (e, stk) { logger.severe('Error during sync', e, stk); } @@ -679,7 +715,7 @@ class StreamChatClient { final updateData = _mapChannelStateToChannel(channels); - await _chatPersistenceClient?.updateChannelQueries( + await chatPersistenceClient?.updateChannelQueries( filter, channels.map((c) => c.channel!.cid).toList(), clearQueryCache: paginationParams.offset == 0, @@ -698,7 +734,7 @@ class StreamChatClient { List>? channelStateSort, PaginationParams paginationParams = const PaginationParams(), }) async { - final offlineChannels = (await _chatPersistenceClient?.getChannelStates( + final offlineChannels = (await chatPersistenceClient?.getChannelStates( filter: filter, // ignore: deprecated_member_use_from_same_package sort: sort, @@ -1362,7 +1398,7 @@ class StreamChatClient { final response = await _chatApi.message.deleteMessage(messageId, hard: hard); if (hard == true) { - await _chatPersistenceClient?.deleteMessageById(messageId); + await chatPersistenceClient?.deleteMessageById(messageId); } return response; } @@ -1468,34 +1504,33 @@ class StreamChatClient { Future disconnectUser({bool flushChatPersistence = false}) async { logger.info('Disconnecting user : ${state.currentUser?.id}'); - // resetting state + // resetting state. state.dispose(); state = ClientState(this); _lastSyncedAt = null; - // resetting credentials + // resetting credentials. _tokenManager.reset(); _connectionIdManager.reset(); - // disconnecting persistence client - await _chatPersistenceClient?.disconnect(flush: flushChatPersistence); - _chatPersistenceClient = null; + // closing persistence connection. + await closePersistenceConnection(flush: flushChatPersistence); // closing web-socket connection - closeConnection(); + return closeConnection(); } /// Call this function to dispose the client Future dispose() async { logger.info('Disposing new StreamChatClient'); - // disposing state + // disposing state. state.dispose(); - // disconnecting persistence client - await _chatPersistenceClient?.disconnect(); + // closing persistence connection. + await closePersistenceConnection(); - // closing web-socket connection + // closing web-socket connection. closeConnection(); await _eventController.close(); diff --git a/packages/stream_chat/lib/src/core/error/stream_chat_error.dart b/packages/stream_chat/lib/src/core/error/stream_chat_error.dart index 1df28558..333211f2 100644 --- a/packages/stream_chat/lib/src/core/error/stream_chat_error.dart +++ b/packages/stream_chat/lib/src/core/error/stream_chat_error.dart @@ -89,8 +89,13 @@ class StreamChatNetworkError extends StreamChatError { }) : super(message); /// - factory StreamChatNetworkError.fromDioError(DioError error) { - final response = error.response; + @Deprecated('Use `StreamChatNetworkError.fromDioException` instead') + factory StreamChatNetworkError.fromDioError(DioException error) = + StreamChatNetworkError.fromDioException; + + /// + factory StreamChatNetworkError.fromDioException(DioException exception) { + final response = exception.response; ErrorResponse? errorResponse; final data = response?.data; if (data != null) { @@ -100,12 +105,12 @@ class StreamChatNetworkError extends StreamChatError { code: errorResponse?.code ?? -1, message: errorResponse?.message ?? response?.statusMessage ?? - error.message ?? + exception.message ?? '', statusCode: errorResponse?.statusCode ?? response?.statusCode, data: errorResponse, - isRequestCancelledError: error.type == DioErrorType.cancel, - )..stackTrace = error.stackTrace; + isRequestCancelledError: exception.type == DioExceptionType.cancel, + )..stackTrace = exception.stackTrace; } /// Error code diff --git a/packages/stream_chat/lib/src/core/http/interceptor/auth_interceptor.dart b/packages/stream_chat/lib/src/core/http/interceptor/auth_interceptor.dart index 759e8e79..7bd9a957 100644 --- a/packages/stream_chat/lib/src/core/http/interceptor/auth_interceptor.dart +++ b/packages/stream_chat/lib/src/core/http/interceptor/auth_interceptor.dart @@ -46,26 +46,26 @@ class AuthInterceptor extends QueuedInterceptor { @override void onError( - DioError err, + DioException exception, ErrorInterceptorHandler handler, ) async { - final data = err.response?.data; + final data = exception.response?.data; if (data == null || data is! Map) { - return handler.next(err); + return handler.next(exception); } final error = ErrorResponse.fromJson(data); if (error.code == ChatErrorCode.tokenExpired.code) { - if (_tokenManager.isStatic) return handler.next(err); + if (_tokenManager.isStatic) return handler.next(exception); await _tokenManager.loadToken(refresh: true); try { - final options = err.requestOptions; + final options = exception.requestOptions; final response = await _client.fetch(options); return handler.resolve(response); - } on DioError catch (error) { - return handler.next(error); + } on DioException catch (exception) { + return handler.next(exception); } } - return handler.next(err); + return handler.next(exception); } } diff --git a/packages/stream_chat/lib/src/core/http/interceptor/logging_interceptor.dart b/packages/stream_chat/lib/src/core/http/interceptor/logging_interceptor.dart index 72fd70d0..6d6f74c3 100644 --- a/packages/stream_chat/lib/src/core/http/interceptor/logging_interceptor.dart +++ b/packages/stream_chat/lib/src/core/http/interceptor/logging_interceptor.dart @@ -119,32 +119,32 @@ class LoggingInterceptor extends Interceptor { } @override - void onError(DioError err, ErrorInterceptorHandler handler) { + void onError(DioException exception, ErrorInterceptorHandler handler) { if (error) { - if (err.type == DioErrorType.badResponse) { - final uri = err.response?.requestOptions.uri; + if (exception.type == DioExceptionType.badResponse) { + final uri = exception.response?.requestOptions.uri; _printBoxed( _logPrintError, header: - 'DioError ║ Status: ${err.response?.statusCode} ${err.response?.statusMessage}', + 'DioException ║ Status: ${exception.response?.statusCode} ${exception.response?.statusMessage}', text: uri.toString(), ); - if (err.response != null && err.response?.data != null) { - _logPrintError('╔ ${err.type.toString()}'); - _printResponse(_logPrintError, err.response!); + if (exception.response != null && exception.response?.data != null) { + _logPrintError('╔ ${exception.type.toString()}'); + _printResponse(_logPrintError, exception.response!); } _printLine(_logPrintError, '╚'); _logPrintError(''); } else { _printBoxed( _logPrintError, - header: 'DioError ║ ${err.type}', - text: err.message, + header: 'DioException ║ ${exception.type}', + text: exception.message, ); - _printRequestHeader(_logPrintError, err.requestOptions); + _printRequestHeader(_logPrintError, exception.requestOptions); } } - super.onError(err, handler); + super.onError(exception, handler); } @override diff --git a/packages/stream_chat/lib/src/core/http/stream_chat_dio_error.dart b/packages/stream_chat/lib/src/core/http/stream_chat_dio_error.dart index b494671c..5b6f91a0 100644 --- a/packages/stream_chat/lib/src/core/http/stream_chat_dio_error.dart +++ b/packages/stream_chat/lib/src/core/http/stream_chat_dio_error.dart @@ -2,7 +2,7 @@ import 'package:dio/dio.dart'; import 'package:stream_chat/src/core/error/error.dart'; /// Error class specific to StreamChat and Dio -class StreamChatDioError extends DioError { +class StreamChatDioError extends DioException { /// Initialize a stream chat dio error StreamChatDioError({ required this.error, diff --git a/packages/stream_chat/lib/src/core/http/stream_http_client.dart b/packages/stream_chat/lib/src/core/http/stream_http_client.dart index 99e72339..0d21f973 100644 --- a/packages/stream_chat/lib/src/core/http/stream_http_client.dart +++ b/packages/stream_chat/lib/src/core/http/stream_http_client.dart @@ -92,16 +92,16 @@ class StreamHttpClient { /// calling [close] will throw an exception. void close({bool force = false}) => httpClient.close(force: force); - StreamChatNetworkError _parseError(DioError err) { + StreamChatNetworkError _parseError(DioException exception) { StreamChatNetworkError error; // locally thrown dio error - if (err is StreamChatDioError) { - error = err.error; + if (exception is StreamChatDioError) { + error = exception.error; } else { // real network request dio error - error = StreamChatNetworkError.fromDioError(err); + error = StreamChatNetworkError.fromDioException(exception); } - return error..stackTrace = err.stackTrace; + return error..stackTrace = exception.stackTrace; } /// Handy method to make http GET request with error parsing. @@ -121,7 +121,7 @@ class StreamHttpClient { cancelToken: cancelToken, ); return response; - } on DioError catch (error) { + } on DioException catch (error) { throw _parseError(error); } } @@ -147,7 +147,7 @@ class StreamHttpClient { cancelToken: cancelToken, ); return response; - } on DioError catch (error) { + } on DioException catch (error) { throw _parseError(error); } } @@ -167,7 +167,7 @@ class StreamHttpClient { cancelToken: cancelToken, ); return response; - } on DioError catch (error) { + } on DioException catch (error) { throw _parseError(error); } } @@ -193,7 +193,7 @@ class StreamHttpClient { cancelToken: cancelToken, ); return response; - } on DioError catch (error) { + } on DioException catch (error) { throw _parseError(error); } } @@ -219,7 +219,7 @@ class StreamHttpClient { cancelToken: cancelToken, ); return response; - } on DioError catch (error) { + } on DioException catch (error) { throw _parseError(error); } } @@ -268,7 +268,7 @@ class StreamHttpClient { cancelToken: cancelToken, ); return response; - } on DioError catch (error) { + } on DioException catch (error) { throw _parseError(error); } } @@ -281,7 +281,7 @@ class StreamHttpClient { try { final response = await httpClient.fetch(requestOptions); return response; - } on DioError catch (error) { + } on DioException catch (error) { throw _parseError(error); } } diff --git a/packages/stream_chat/lib/src/db/chat_persistence_client.dart b/packages/stream_chat/lib/src/db/chat_persistence_client.dart index 4710190e..bf0b1a76 100644 --- a/packages/stream_chat/lib/src/db/chat_persistence_client.dart +++ b/packages/stream_chat/lib/src/db/chat_persistence_client.dart @@ -17,6 +17,11 @@ abstract class ChatPersistenceClient { /// Whether the connection is established. bool get isConnected; + /// The current user id to which the client is connected. + /// + /// Returns `null` if the client is not connected. + String? get userId; + /// Creates a new connection to the client Future connect(String userId); diff --git a/packages/stream_chat/lib/stream_chat.dart b/packages/stream_chat/lib/stream_chat.dart index 1e0df28c..2dbf1afd 100644 --- a/packages/stream_chat/lib/stream_chat.dart +++ b/packages/stream_chat/lib/stream_chat.dart @@ -1,12 +1,17 @@ library stream_chat; export 'package:async/async.dart'; -export 'package:dio/src/cancel_token.dart'; -export 'package:dio/src/dio_error.dart'; -export 'package:dio/src/dio_mixin.dart' show Interceptor, InterceptorsWrapper; -export 'package:dio/src/multipart_file.dart'; -export 'package:dio/src/options.dart'; -export 'package:dio/src/options.dart' show ProgressCallback; +export 'package:dio/dio.dart' + show + DioException, + DioExceptionType, + RequestOptions, + CancelToken, + Interceptor, + InterceptorsWrapper, + MultipartFile, + Options, + ProgressCallback; export 'package:logging/logging.dart' show Logger, Level, LogRecord; export 'package:rate_limiter/rate_limiter.dart'; export 'package:uuid/uuid.dart'; @@ -17,7 +22,6 @@ export 'src/client/key_stroke_handler.dart'; export 'src/core/api/attachment_file_uploader.dart'; export 'src/core/api/requests.dart'; export 'src/core/api/responses.dart'; -export 'src/core/api/stream_chat_api.dart' show PushProvider; export 'src/core/api/stream_chat_api.dart'; export 'src/core/error/error.dart'; export 'src/core/http/interceptor/logging_interceptor.dart'; diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index 62bf7b52..cbd95b88 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -11,7 +11,7 @@ environment: dependencies: async: ^2.10.0 collection: ^1.17.0 - dio: ^5.1.1 + dio: ^5.2.0 equatable: ^2.0.5 freezed_annotation: ^2.2.0 http_parser: ^4.0.2 diff --git a/packages/stream_chat/test/src/client/client_test.dart b/packages/stream_chat/test/src/client/client_test.dart index 7a605557..9ff0c69f 100644 --- a/packages/stream_chat/test/src/client/client_test.dart +++ b/packages/stream_chat/test/src/client/client_test.dart @@ -2544,4 +2544,124 @@ void main() { }, ); }); + + group('PersistenceConnectionTests', () { + const apiKey = 'test-api-key'; + late final api = FakeChatApi(); + late final ws = FakeWebSocket(); + + final user = User(id: 'test-user-id'); + final token = Token.development(user.id).rawValue; + + late StreamChatClient client; + + setUp(() async { + client = StreamChatClient(apiKey, chatApi: api, ws: ws); + expect(client.persistenceEnabled, isFalse); + }); + + tearDown(() { + client.chatPersistenceClient = null; + expect(client.persistenceEnabled, isFalse); + client.dispose(); + }); + + test('openPersistenceConnection connects the client to the user', () async { + client.chatPersistenceClient = MockPersistenceClient(); + await client.openPersistenceConnection(user); + expect(client.persistenceEnabled, isTrue); + }); + + test( + '''multiple call to openPersistenceConnection does not throws an error if already connected to the same user''', + () async { + client.chatPersistenceClient = MockPersistenceClient(); + await client.openPersistenceConnection(user); + expect(client.persistenceEnabled, isTrue); + + await expectLater(client.openPersistenceConnection(user), completes); + await expectLater(client.openPersistenceConnection(user), completes); + await expectLater(client.openPersistenceConnection(user), completes); + }, + ); + + test( + '''openPersistenceConnection throws an error if client is already connected to a different user''', + () async { + client.chatPersistenceClient = MockPersistenceClient(); + await client.openPersistenceConnection(user); + expect(client.persistenceEnabled, isTrue); + + await expectLater( + client.openPersistenceConnection(user.copyWith(id: 'new-id')), + throwsA(const TypeMatcher()), + ); + }, + ); + + test( + '''openPersistenceConnection throws an error if chatPersistenceClient is not set''', + () async { + await expectLater( + client.openPersistenceConnection(user), + throwsA(const TypeMatcher()), + ); + }, + ); + + test('closePersistenceConnection disconnects the client', () async { + client.chatPersistenceClient = MockPersistenceClient(); + await client.openPersistenceConnection(user); + expect(client.persistenceEnabled, isTrue); + + await client.closePersistenceConnection(); + expect(client.persistenceEnabled, isFalse); + }); + + test( + '''closePersistenceConnection compeletes normally if chatPersistenceClient is not connected''', + () async { + client.chatPersistenceClient = MockPersistenceClient(); + expect(client.chatPersistenceClient!.isConnected, isFalse); + + await expectLater(client.closePersistenceConnection(), completes); + }, + ); + + test( + '''closePersistenceConnection completes normally if chatPersistenceClient is not set''', + () async { + expect(client.persistenceEnabled, isFalse); + await expectLater(client.closePersistenceConnection(), completes); + }, + ); + + test( + '''connectUser completes normally if the persistence connection is already connected to the same user''', + () async { + client.chatPersistenceClient = MockPersistenceClient(); + await client.openPersistenceConnection(user); + expect(client.persistenceEnabled, isTrue); + + await expectLater( + client.connectUser(user, token, connectWebSocket: false), + completes, + ); + }, + ); + + test( + '''connectUser should throw if the persistence connection if already connected to a different user''', + () async { + client.chatPersistenceClient = MockPersistenceClient(); + await client.openPersistenceConnection(user.copyWith(id: 'new-id')); + expect(client.persistenceEnabled, isTrue); + + await expectLater( + client.connectUser(user, token, connectWebSocket: false), + throwsA(const TypeMatcher()), + ); + }, + ); + }); } diff --git a/packages/stream_chat/test/src/core/error/stream_chat_error_test.dart b/packages/stream_chat/test/src/core/error/stream_chat_error_test.dart index a32e0d71..8d4e5465 100644 --- a/packages/stream_chat/test/src/core/error/stream_chat_error_test.dart +++ b/packages/stream_chat/test/src/core/error/stream_chat_error_test.dart @@ -60,7 +60,7 @@ void main() { expect(error.message, message); }); - test('.fromDioError', () { + test('.fromDioException', () { const code = 333; const statusCode = 666; const message = 'test-error-message'; @@ -69,7 +69,7 @@ void main() { ..code = code ..statusCode = statusCode ..message = message; - final dioError = DioError( + final dioError = DioException( requestOptions: options, response: Response( requestOptions: options, @@ -77,7 +77,7 @@ void main() { data: data.toJson(), ), ); - final error = StreamChatNetworkError.fromDioError(dioError); + final error = StreamChatNetworkError.fromDioException(dioError); expect(error, isNotNull); expect(error.code, code); expect(error.message, message); diff --git a/packages/stream_chat/test/src/core/http/interceptor/auth_interceptor_test.dart b/packages/stream_chat/test/src/core/http/interceptor/auth_interceptor_test.dart index 7bd81ff0..e5d06600 100644 --- a/packages/stream_chat/test/src/core/http/interceptor/auth_interceptor_test.dart +++ b/packages/stream_chat/test/src/core/http/interceptor/auth_interceptor_test.dart @@ -88,7 +88,7 @@ void main() { requestOptions: options, data: errorResponse.toJson(), ); - final err = DioError(requestOptions: options, response: response); + final err = DioException(requestOptions: options, response: response); final handler = ErrorInterceptorHandler(); when(() => tokenManager.isStatic).thenReturn(false); @@ -135,7 +135,7 @@ void main() { requestOptions: options, data: errorResponse.toJson(), ); - final err = DioError(requestOptions: options, response: response); + final err = DioException(requestOptions: options, response: response); final handler = ErrorInterceptorHandler(); when(() => tokenManager.isStatic).thenReturn(false); @@ -153,7 +153,7 @@ void main() { } catch (e) { // need to cast it as the type is private in dio final error = (e as dynamic).data; - expect(error, isA()); + expect(error, isA()); } verify(() => tokenManager.isStatic).called(1); @@ -179,7 +179,7 @@ void main() { requestOptions: options, data: errorResponse.toJson(), ); - final err = DioError(requestOptions: options, response: response); + final err = DioException(requestOptions: options, response: response); final handler = ErrorInterceptorHandler(); when(() => tokenManager.isStatic).thenReturn(true); @@ -191,8 +191,8 @@ void main() { } catch (e) { // need to cast it as the type is private in dio final error = (e as dynamic).data; - expect(error, isA()); - final response = StreamChatNetworkError.fromDioError(error); + expect(error, isA()); + final response = StreamChatNetworkError.fromDioException(error); expect(response.errorCode, code); } @@ -207,7 +207,7 @@ void main() { const path = 'test-request-path'; final options = RequestOptions(path: path); final response = Response(requestOptions: options); - final err = DioError(requestOptions: options, response: response); + final err = DioException(requestOptions: options, response: response); final handler = ErrorInterceptorHandler(); authInterceptor.onError(err, handler); @@ -217,7 +217,7 @@ void main() { } catch (e) { // need to cast it as the type is private in dio final error = (e as dynamic).data; - expect(error, isA()); + expect(error, isA()); } }, ); diff --git a/packages/stream_chat/test/src/core/http/stream_chat_dio_error_test.dart b/packages/stream_chat/test/src/core/http/stream_chat_dio_error_test.dart index f13cf50d..e29148bf 100644 --- a/packages/stream_chat/test/src/core/http/stream_chat_dio_error_test.dart +++ b/packages/stream_chat/test/src/core/http/stream_chat_dio_error_test.dart @@ -12,7 +12,7 @@ void main() { requestOptions: options, ); - expect(dioError, isA()); + expect(dioError, isA()); expect(dioError, isNotNull); expect(dioError.error, error); expect(dioError.requestOptions, options); diff --git a/packages/stream_chat/test/src/core/http/stream_http_client_test.dart b/packages/stream_chat/test/src/core/http/stream_http_client_test.dart index 51533b11..f63e08f1 100644 --- a/packages/stream_chat/test/src/core/http/stream_http_client_test.dart +++ b/packages/stream_chat/test/src/core/http/stream_http_client_test.dart @@ -21,7 +21,7 @@ void main() { statusCode: 200, ); - DioError throwableError( + DioException throwableError( String path, { StreamChatNetworkError? error, bool streamChatDioError = false, @@ -32,11 +32,11 @@ void main() { ..code = error?.code ..statusCode = error?.statusCode ..message = error?.message; - DioError? dioError; + DioException? dioError; if (streamChatDioError) { dioError = StreamChatDioError(error: error!, requestOptions: options); } else { - dioError = DioError( + dioError = DioException( error: error, requestOptions: options, response: Response( @@ -210,7 +210,7 @@ void main() { await client.get(path); } catch (e) { expect(e, isA()); - expect(e, StreamChatNetworkError.fromDioError(error)); + expect(e, StreamChatNetworkError.fromDioException(error)); } verify(() => dio.get( @@ -263,7 +263,7 @@ void main() { await client.post(path); } catch (e) { expect(e, isA()); - expect(e, StreamChatNetworkError.fromDioError(error)); + expect(e, StreamChatNetworkError.fromDioException(error)); } verify(() => dio.post( @@ -317,7 +317,7 @@ void main() { await client.delete(path); } catch (e) { expect(e, isA()); - expect(e, StreamChatNetworkError.fromDioError(error)); + expect(e, StreamChatNetworkError.fromDioException(error)); } verify(() => dio.delete( @@ -371,7 +371,7 @@ void main() { await client.patch(path); } catch (e) { expect(e, isA()); - expect(e, StreamChatNetworkError.fromDioError(error)); + expect(e, StreamChatNetworkError.fromDioException(error)); } verify(() => dio.patch( @@ -425,7 +425,7 @@ void main() { await client.put(path); } catch (e) { expect(e, isA()); - expect(e, StreamChatNetworkError.fromDioError(error)); + expect(e, StreamChatNetworkError.fromDioException(error)); } verify(() => dio.put( @@ -486,7 +486,7 @@ void main() { await client.postFile(path, file); } catch (e) { expect(e, isA()); - expect(e, StreamChatNetworkError.fromDioError(error)); + expect(e, StreamChatNetworkError.fromDioException(error)); } verify(() => dio.post( diff --git a/packages/stream_chat/test/src/db/chat_persistence_client_test.dart b/packages/stream_chat/test/src/db/chat_persistence_client_test.dart index 62a7cf25..a0a12935 100644 --- a/packages/stream_chat/test/src/db/chat_persistence_client_test.dart +++ b/packages/stream_chat/test/src/db/chat_persistence_client_test.dart @@ -15,6 +15,9 @@ class TestPersistenceClient extends ChatPersistenceClient { @override bool get isConnected => throw UnimplementedError(); + @override + String? get userId => throw UnimplementedError(); + @override Future connect(String userId) => throw UnimplementedError(); diff --git a/packages/stream_chat/test/src/mocks.dart b/packages/stream_chat/test/src/mocks.dart index 44078519..f412d15a 100644 --- a/packages/stream_chat/test/src/mocks.dart +++ b/packages/stream_chat/test/src/mocks.dart @@ -64,11 +64,26 @@ class MockAttachmentFileUploader extends Mock implements AttachmentFileUploader {} class MockPersistenceClient extends Mock implements ChatPersistenceClient { - @override - Future connect(String userId) => Future.value(); + String? _userId; + bool _isConnected = false; @override - Future disconnect({bool flush = false}) => Future.value(); + bool get isConnected => _isConnected; + + @override + String? get userId => _userId; + + @override + Future connect(String userId) async { + _userId = userId; + _isConnected = true; + } + + @override + Future disconnect({bool flush = false}) async { + _userId = null; + _isConnected = false; + } } class MockStreamChatClient extends Mock implements StreamChatClient { diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 0ea80c8f..2e1b875f 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,3 +1,9 @@ +## Upcoming + +🔄 Changed + +- Updated `dio` dependency to `^5.2.0`. + ## 6.2.0 🐞 Fixed diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index c360f1ec..03ee2b22 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: dart_vlc: ^0.4.0 desktop_drop: ^0.4.0 diacritic: ^0.1.3 - dio: ^5.1.1 + dio: ^5.2.0 ezanimation: ^0.6.0 file_picker: ^5.2.4 file_selector: ^0.9.0 diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index c7869125..0c178fd4 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -1,3 +1,7 @@ +## Upcoming + +- Updated `stream_chat` dependency to [`Upcoming`](https://pub.dev/packages/stream_chat/changelog). + ## 6.2.0 - Fixed `StreamMessageInputController.textPatternStyle` not matching case-insensitive patterns. diff --git a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart index 9cfc3464..5bb8dee7 100644 --- a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart +++ b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart @@ -430,16 +430,15 @@ class StreamChannelState extends State { ], builder: (context, snapshot) { if (snapshot.hasError) { - var message = snapshot.error.toString(); - if (snapshot.error is DioError) { - final dioError = snapshot.error as DioError?; - if (dioError?.type == DioErrorType.badResponse) { - message = dioError!.message ?? 'Bad response'; - } else { - message = 'Check your connection and retry'; + final error = snapshot.error; + if (error is DioException) { + if (error.type == DioExceptionType.badResponse) { + return Center(child: Text(error.message ?? 'Bad response')); } + return const Center(child: Text('Check your connection and retry')); } - return Center(child: Text(message)); + + return Center(child: Text(error.toString())); } final dataLoaded = snapshot.data?.every((it) => it) == true; diff --git a/packages/stream_chat_flutter_core/test/stream_channel_test.dart b/packages/stream_chat_flutter_core/test/stream_channel_test.dart index f4acf3c9..5ba31042 100644 --- a/packages/stream_chat_flutter_core/test/stream_channel_test.dart +++ b/packages/stream_chat_flutter_core/test/stream_channel_test.dart @@ -91,8 +91,8 @@ void main() { ); const errorMessage = 'Error! Error! Error!'; - final error = DioError( - type: DioErrorType.badResponse, + final error = DioException( + type: DioExceptionType.badResponse, message: errorMessage, requestOptions: RequestOptions(), ); diff --git a/packages/stream_chat_persistence/CHANGELOG.md b/packages/stream_chat_persistence/CHANGELOG.md index 40ee9481..7254a957 100644 --- a/packages/stream_chat_persistence/CHANGELOG.md +++ b/packages/stream_chat_persistence/CHANGELOG.md @@ -4,6 +4,8 @@ - [[#1422]](https://github.com/GetStream/stream-chat-flutter/issues/1422) Removed default values from `UserEntity` `createdAt` and `updatedAt` fields. - Updated `stream_chat` dependency to [`6.2.0`](https://pub.dev/packages/stream_chat/changelog). +- Added support for `StreamChatPersistenceClient.openPersistenceConnection` + and `StreamChatPersistenceClient.closePersistenceConnection` for opening and closing the database connection. ## 6.1.0 diff --git a/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart b/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart index 374bfa59..e8737baa 100644 --- a/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart +++ b/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart @@ -82,6 +82,9 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { @override bool get isConnected => db != null; + @override + String? get userId => db?.userId; + @override Future connect( String userId, { diff --git a/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart b/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart index 6791e07c..0f3223a6 100644 --- a/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart +++ b/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart @@ -20,7 +20,7 @@ void main() { await client.connect(userId, databaseProvider: testDatabaseProvider); expect(client.isConnected, true); expect(client.db, isA()); - expect(client.db!.userId, userId); + expect(client.userId, userId); addTearDown(() async { await client.disconnect(); @@ -33,7 +33,7 @@ void main() { await client.connect(userId, databaseProvider: testDatabaseProvider); expect(client.isConnected, true); expect(client.db, isA()); - expect(client.db!.userId, userId); + expect(client.userId, userId); expect( () => client.connect(userId, databaseProvider: testDatabaseProvider), throwsException,