diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/client/channel.dart similarity index 99% rename from packages/stream_chat/lib/src/api/channel.dart rename to packages/stream_chat/lib/src/client/channel.dart index 3fb63c91..5bc59ae8 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -4,13 +4,12 @@ import 'dart:math'; import 'package:collection/collection.dart' show IterableExtension, ListEquality; import 'package:dio/dio.dart'; -import 'package:logging/logging.dart'; import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat/src/api/retry_queue.dart'; -import 'package:stream_chat/src/core/utils.dart'; -import 'package:stream_chat/src/errors/stream_chat_error.dart'; +import 'package:stream_chat/src/client/retry_queue.dart'; +import 'package:stream_chat/src/core/util/utils.dart'; +import 'package:stream_chat/src/core/error/error.dart'; import 'package:stream_chat/src/event_type.dart'; -import 'package:stream_chat/src/extensions/rate_limit.dart'; +import 'package:rate_limiter/rate_limiter.dart'; import 'package:stream_chat/src/core/models/attachment_file.dart'; import 'package:stream_chat/src/core/models/channel_state.dart'; import 'package:stream_chat/src/core/models/user.dart'; diff --git a/packages/stream_chat/lib/src/client.dart b/packages/stream_chat/lib/src/client/client.dart similarity index 99% rename from packages/stream_chat/lib/src/client.dart rename to packages/stream_chat/lib/src/client/client.dart index e7e05b69..ddc6e335 100644 --- a/packages/stream_chat/lib/src/client.dart +++ b/packages/stream_chat/lib/src/client/client.dart @@ -1,20 +1,19 @@ // ignore_for_file: unnecessary_getters_setters import 'dart:async'; -import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:logging/logging.dart'; import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat/src/api/channel.dart'; -import 'package:stream_chat/src/core/utils.dart'; -import 'package:stream_chat/src/errors/stream_chat_error.dart'; +import 'package:stream_chat/src/client/channel.dart'; +import 'package:stream_chat/src/core/util/utils.dart'; +import 'package:stream_chat/src/core/error/error.dart'; import 'package:stream_chat/src/location.dart'; import 'package:stream_chat/src/ws/connection_status.dart'; import 'package:stream_chat/src/core/api/attachment_file_uploader.dart'; import 'package:stream_chat/src/core/api/requests.dart'; import 'package:stream_chat/src/core/api/responses.dart'; -import 'package:stream_chat/src/api/retry_policy.dart'; +import 'package:stream_chat/src/client/retry_policy.dart'; import 'package:stream_chat/src/ws/websocket.dart'; import 'package:stream_chat/src/core/api/stream_chat_api.dart'; import 'package:stream_chat/src/core/http/connection_id_manager.dart'; diff --git a/packages/stream_chat/lib/src/api/retry_policy.dart b/packages/stream_chat/lib/src/client/retry_policy.dart similarity index 87% rename from packages/stream_chat/lib/src/api/retry_policy.dart rename to packages/stream_chat/lib/src/client/retry_policy.dart index 14933fed..fbebc696 100644 --- a/packages/stream_chat/lib/src/api/retry_policy.dart +++ b/packages/stream_chat/lib/src/client/retry_policy.dart @@ -1,5 +1,5 @@ -import 'package:stream_chat/src/client.dart'; -import 'package:stream_chat/src/errors/stream_chat_error.dart'; +import 'package:stream_chat/src/client/client.dart'; +import 'package:stream_chat/src/core/error/error.dart'; /// The retry options class RetryPolicy { diff --git a/packages/stream_chat/lib/src/api/retry_queue.dart b/packages/stream_chat/lib/src/client/retry_queue.dart similarity index 97% rename from packages/stream_chat/lib/src/api/retry_queue.dart rename to packages/stream_chat/lib/src/client/retry_queue.dart index 9d0b8502..008270e5 100644 --- a/packages/stream_chat/lib/src/api/retry_queue.dart +++ b/packages/stream_chat/lib/src/client/retry_queue.dart @@ -3,9 +3,9 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:logging/logging.dart'; import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat/src/api/channel.dart'; -import 'package:stream_chat/src/api/retry_policy.dart'; -import 'package:stream_chat/src/errors/stream_chat_error.dart'; +import 'package:stream_chat/src/client/channel.dart'; +import 'package:stream_chat/src/client/retry_policy.dart'; +import 'package:stream_chat/src/core/error/error.dart'; import 'package:stream_chat/src/event_type.dart'; import 'package:stream_chat/src/core/models/message.dart'; import 'package:stream_chat/stream_chat.dart'; diff --git a/packages/stream_chat/lib/src/core/api/attachment_file_uploader.dart b/packages/stream_chat/lib/src/core/api/attachment_file_uploader.dart index 272188af..8eacf716 100644 --- a/packages/stream_chat/lib/src/core/api/attachment_file_uploader.dart +++ b/packages/stream_chat/lib/src/core/api/attachment_file_uploader.dart @@ -1,7 +1,7 @@ import 'package:dio/dio.dart'; import 'package:stream_chat/src/core/api/responses.dart'; import 'package:stream_chat/src/core/http/stream_http_client.dart'; -import 'package:stream_chat/src/extensions/string_extension.dart'; +import 'package:stream_chat/src/core/util/extension.dart'; import 'package:stream_chat/src/core/models/attachment_file.dart'; /// Class responsible for uploading images and files to a given channel diff --git a/packages/stream_chat/lib/src/core/api/responses.dart b/packages/stream_chat/lib/src/core/api/responses.dart index c639a960..b6d6e801 100644 --- a/packages/stream_chat/lib/src/core/api/responses.dart +++ b/packages/stream_chat/lib/src/core/api/responses.dart @@ -1,5 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:stream_chat/src/client.dart'; +import 'package:stream_chat/src/client/client.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/device.dart'; @@ -9,7 +9,7 @@ import 'package:stream_chat/src/core/models/message.dart'; import 'package:stream_chat/src/core/models/reaction.dart'; import 'package:stream_chat/src/core/models/read.dart'; import 'package:stream_chat/src/core/models/user.dart'; -import 'package:stream_chat/src/errors/stream_chat_error.dart'; +import 'package:stream_chat/src/core/error/error.dart'; part 'responses.g.dart'; diff --git a/packages/stream_chat/lib/src/errors/chat_error_code.dart b/packages/stream_chat/lib/src/core/error/chat_error_code.dart similarity index 100% rename from packages/stream_chat/lib/src/errors/chat_error_code.dart rename to packages/stream_chat/lib/src/core/error/chat_error_code.dart diff --git a/packages/stream_chat/lib/src/core/error/error.dart b/packages/stream_chat/lib/src/core/error/error.dart new file mode 100644 index 00000000..6f2c39a3 --- /dev/null +++ b/packages/stream_chat/lib/src/core/error/error.dart @@ -0,0 +1,2 @@ +export 'chat_error_code.dart'; +export 'stream_chat_error.dart'; \ No newline at end of file diff --git a/packages/stream_chat/lib/src/errors/stream_chat_error.dart b/packages/stream_chat/lib/src/core/error/stream_chat_error.dart similarity index 97% rename from packages/stream_chat/lib/src/errors/stream_chat_error.dart rename to packages/stream_chat/lib/src/core/error/stream_chat_error.dart index a057783a..04214033 100644 --- a/packages/stream_chat/lib/src/errors/stream_chat_error.dart +++ b/packages/stream_chat/lib/src/core/error/stream_chat_error.dart @@ -1,5 +1,5 @@ import 'package:equatable/equatable.dart'; -import 'package:stream_chat/src/errors/chat_error_code.dart'; +import 'package:stream_chat/src/core/error/chat_error_code.dart'; import 'package:stream_chat/stream_chat.dart'; /// 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 9dafc6c9..d689cce1 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 @@ -5,8 +5,8 @@ import 'package:stream_chat/src/core/http/stream_http_client.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/errors/chat_error_code.dart'; -import 'package:stream_chat/src/errors/stream_chat_error.dart'; +import 'package:stream_chat/src/core/error/error.dart'; + /// class AuthInterceptor extends Interceptor { 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 f0e61111..987c9d67 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 @@ -1,5 +1,5 @@ import 'package:dio/dio.dart'; -import 'package:stream_chat/src/errors/stream_chat_error.dart'; +import 'package:stream_chat/src/core/error/error.dart'; /// class StreamChatDioError extends DioError { 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 0daeaacd..3eacd11e 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 @@ -8,9 +8,9 @@ import 'package:stream_chat/src/core/http/interceptor/connection_id_interceptor. import 'package:stream_chat/src/core/http/interceptor/logging_interceptor.dart'; import 'package:stream_chat/src/core/http/stream_chat_dio_error.dart'; import 'package:stream_chat/src/core/http/token_manager.dart'; -import 'package:stream_chat/src/errors/stream_chat_error.dart'; +import 'package:stream_chat/src/core/error/error.dart'; import 'package:stream_chat/src/location.dart'; -import 'package:stream_chat/src/platform_detector/platform_detector.dart'; +import 'package:stream_chat/src/core/platform_detector/platform_detector.dart'; import 'package:stream_chat/version.dart'; import 'package:stream_chat/src/core/http/connection_id_manager.dart'; diff --git a/packages/stream_chat/lib/src/core/http/token.dart b/packages/stream_chat/lib/src/core/http/token.dart index a914521c..26532e80 100644 --- a/packages/stream_chat/lib/src/core/http/token.dart +++ b/packages/stream_chat/lib/src/core/http/token.dart @@ -3,7 +3,7 @@ import 'dart:convert'; import 'package:equatable/equatable.dart'; import 'package:jose/jose.dart'; import 'package:stream_chat/src/core/models/user.dart'; -import 'package:stream_chat/src/core/utils.dart'; +import 'package:stream_chat/src/core/util/utils.dart'; /// typedef GuestTokenProvider = Future Function(User user); diff --git a/packages/stream_chat/lib/src/core/models/attachment.dart b/packages/stream_chat/lib/src/core/models/attachment.dart index 1df785a8..a5f9f19d 100644 --- a/packages/stream_chat/lib/src/core/models/attachment.dart +++ b/packages/stream_chat/lib/src/core/models/attachment.dart @@ -4,7 +4,7 @@ import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:stream_chat/src/core/models/action.dart'; import 'package:stream_chat/src/core/models/attachment_file.dart'; -import 'package:stream_chat/src/core/models/serialization.dart'; +import 'package:stream_chat/src/core/util/serialization.dart'; import 'package:uuid/uuid.dart'; part 'attachment.g.dart'; diff --git a/packages/stream_chat/lib/src/core/models/channel_model.dart b/packages/stream_chat/lib/src/core/models/channel_model.dart index 892713e7..9e50ed3e 100644 --- a/packages/stream_chat/lib/src/core/models/channel_model.dart +++ b/packages/stream_chat/lib/src/core/models/channel_model.dart @@ -1,6 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:stream_chat/src/core/models/channel_config.dart'; -import 'package:stream_chat/src/core/models/serialization.dart'; +import 'package:stream_chat/src/core/util/serialization.dart'; import 'package:stream_chat/src/core/models/user.dart'; part 'channel_model.g.dart'; diff --git a/packages/stream_chat/lib/src/core/models/event.dart b/packages/stream_chat/lib/src/core/models/event.dart index 6ed1d414..7c1be679 100644 --- a/packages/stream_chat/lib/src/core/models/event.dart +++ b/packages/stream_chat/lib/src/core/models/event.dart @@ -1,7 +1,7 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:stream_chat/src/core/models/channel_model.dart'; import 'package:stream_chat/src/core/models/message.dart'; -import 'package:stream_chat/src/core/models/serialization.dart'; +import 'package:stream_chat/src/core/util/serialization.dart'; import 'package:stream_chat/stream_chat.dart'; part 'event.g.dart'; diff --git a/packages/stream_chat/lib/src/core/models/message.dart b/packages/stream_chat/lib/src/core/models/message.dart index f3bf100f..1266519c 100644 --- a/packages/stream_chat/lib/src/core/models/message.dart +++ b/packages/stream_chat/lib/src/core/models/message.dart @@ -2,7 +2,7 @@ import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:stream_chat/src/core/models/attachment.dart'; import 'package:stream_chat/src/core/models/reaction.dart'; -import 'package:stream_chat/src/core/models/serialization.dart'; +import 'package:stream_chat/src/core/util/serialization.dart'; import 'package:stream_chat/src/core/models/user.dart'; import 'package:uuid/uuid.dart'; @@ -112,7 +112,7 @@ class Message extends Equatable { /// The list of user mentioned in the message @JsonKey( - toJson: Serialization.userIds, + toJson: User.toIds, defaultValue: [], ) final List mentionedUsers; diff --git a/packages/stream_chat/lib/src/core/models/message.g.dart b/packages/stream_chat/lib/src/core/models/message.g.dart index ca094a83..7d268d0e 100644 --- a/packages/stream_chat/lib/src/core/models/message.g.dart +++ b/packages/stream_chat/lib/src/core/models/message.g.dart @@ -85,7 +85,7 @@ Map _$MessageToJson(Message instance) { writeNotNull('type', readonly(instance.type)); val['attachments'] = instance.attachments.map((e) => e.toJson()).toList(); - val['mentioned_users'] = Serialization.userIds(instance.mentionedUsers); + val['mentioned_users'] = User.toIds(instance.mentionedUsers); writeNotNull('reaction_counts', readonly(instance.reactionCounts)); writeNotNull('reaction_scores', readonly(instance.reactionScores)); writeNotNull('latest_reactions', readonly(instance.latestReactions)); diff --git a/packages/stream_chat/lib/src/core/models/mute.dart b/packages/stream_chat/lib/src/core/models/mute.dart index 8c3bef89..7a82fc54 100644 --- a/packages/stream_chat/lib/src/core/models/mute.dart +++ b/packages/stream_chat/lib/src/core/models/mute.dart @@ -1,6 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:stream_chat/src/core/models/channel_model.dart'; -import 'package:stream_chat/src/core/models/serialization.dart'; +import 'package:stream_chat/src/core/util/serialization.dart'; import 'package:stream_chat/src/core/models/user.dart'; part 'mute.g.dart'; diff --git a/packages/stream_chat/lib/src/core/models/own_user.dart b/packages/stream_chat/lib/src/core/models/own_user.dart index 6f975a94..b9cdf0fd 100644 --- a/packages/stream_chat/lib/src/core/models/own_user.dart +++ b/packages/stream_chat/lib/src/core/models/own_user.dart @@ -1,7 +1,7 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:stream_chat/src/core/models/device.dart'; import 'package:stream_chat/src/core/models/mute.dart'; -import 'package:stream_chat/src/core/models/serialization.dart'; +import 'package:stream_chat/src/core/util/serialization.dart'; import 'package:stream_chat/src/core/models/user.dart'; part 'own_user.g.dart'; diff --git a/packages/stream_chat/lib/src/core/models/reaction.dart b/packages/stream_chat/lib/src/core/models/reaction.dart index ace4a15e..997d833c 100644 --- a/packages/stream_chat/lib/src/core/models/reaction.dart +++ b/packages/stream_chat/lib/src/core/models/reaction.dart @@ -1,5 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:stream_chat/src/core/models/serialization.dart'; +import 'package:stream_chat/src/core/util/serialization.dart'; import 'package:stream_chat/src/core/models/user.dart'; part 'reaction.g.dart'; diff --git a/packages/stream_chat/lib/src/core/models/user.dart b/packages/stream_chat/lib/src/core/models/user.dart index ae1dd742..b5fdec69 100644 --- a/packages/stream_chat/lib/src/core/models/user.dart +++ b/packages/stream_chat/lib/src/core/models/user.dart @@ -1,5 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:stream_chat/src/core/models/serialization.dart'; +import 'package:stream_chat/src/core/util/serialization.dart'; part 'user.g.dart'; @@ -92,6 +92,10 @@ class User { return id; } + /// List of users to list of userIds + static List? toIds(List? users) => + users?.map((u) => u.id).toList(); + @override bool operator ==(Object other) => identical(this, other) || diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector.dart b/packages/stream_chat/lib/src/core/platform_detector/platform_detector.dart similarity index 95% rename from packages/stream_chat/lib/src/platform_detector/platform_detector.dart rename to packages/stream_chat/lib/src/core/platform_detector/platform_detector.dart index 58d86db4..cf817cac 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector.dart +++ b/packages/stream_chat/lib/src/core/platform_detector/platform_detector.dart @@ -1,4 +1,4 @@ -import 'package:stream_chat/src/platform_detector/platform_detector_stub.dart' +import 'package:stream_chat/src/core/platform_detector/platform_detector_stub.dart' if (dart.library.html) 'platform_detector_web.dart' if (dart.library.io) 'platform_detector_io.dart'; diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart b/packages/stream_chat/lib/src/core/platform_detector/platform_detector_io.dart similarity index 82% rename from packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart rename to packages/stream_chat/lib/src/core/platform_detector/platform_detector_io.dart index c7b4a0b7..da707eed 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart +++ b/packages/stream_chat/lib/src/core/platform_detector/platform_detector_io.dart @@ -1,5 +1,5 @@ import 'dart:io'; -import 'package:stream_chat/src/platform_detector/platform_detector.dart'; +import 'package:stream_chat/src/core/platform_detector/platform_detector.dart'; /// Version running on native systems PlatformType get currentPlatform { diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart b/packages/stream_chat/lib/src/core/platform_detector/platform_detector_stub.dart similarity index 53% rename from packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart rename to packages/stream_chat/lib/src/core/platform_detector/platform_detector_stub.dart index b9e13c2e..9d1a7f66 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart +++ b/packages/stream_chat/lib/src/core/platform_detector/platform_detector_stub.dart @@ -1,4 +1,4 @@ -import 'package:stream_chat/src/platform_detector/platform_detector.dart'; +import 'package:stream_chat/src/core/platform_detector/platform_detector.dart'; /// Stub implementation PlatformType get currentPlatform { diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart b/packages/stream_chat/lib/src/core/platform_detector/platform_detector_web.dart similarity index 50% rename from packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart rename to packages/stream_chat/lib/src/core/platform_detector/platform_detector_web.dart index ba5d04fc..324b4145 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart +++ b/packages/stream_chat/lib/src/core/platform_detector/platform_detector_web.dart @@ -1,4 +1,4 @@ -import 'package:stream_chat/src/platform_detector/platform_detector.dart'; +import 'package:stream_chat/src/core/platform_detector/platform_detector.dart'; /// Version running on web PlatformType get currentPlatform => PlatformType.web; diff --git a/packages/stream_chat/lib/src/core/util/extension.dart b/packages/stream_chat/lib/src/core/util/extension.dart new file mode 100644 index 00000000..5dd8ea15 --- /dev/null +++ b/packages/stream_chat/lib/src/core/util/extension.dart @@ -0,0 +1,30 @@ +import 'package:http_parser/http_parser.dart'; +import 'package:mime/mime.dart'; + +/// Useful extension functions for [Iterable] +extension IterableX on Iterable { + /// Removes all the null values + /// and converts `Iterable` into `Iterable` + Iterable get withNullifyer => whereType(); +} + +/// Useful extension functions for [Map] +extension MapX on Map { + /// Returns a new map with null keys or values removed + Map get nullProtected => + Map.from(this)..removeWhere((key, value) => key == null || value == null); +} + +/// Useful extension functions for [String] +extension StringX on String { + /// returns the mime type from the passed file name. + MediaType? get mimeType { + if (toLowerCase().endsWith('heic')) { + return MediaType.parse('image/heic'); + } else { + final mimeType = lookupMimeType(this); + if (mimeType == null) return null; + return MediaType.parse(mimeType); + } + } +} diff --git a/packages/stream_chat/lib/src/core/models/serialization.dart b/packages/stream_chat/lib/src/core/util/serialization.dart similarity index 86% rename from packages/stream_chat/lib/src/core/models/serialization.dart rename to packages/stream_chat/lib/src/core/util/serialization.dart index 692ad01a..1e933a32 100644 --- a/packages/stream_chat/lib/src/core/models/serialization.dart +++ b/packages/stream_chat/lib/src/core/util/serialization.dart @@ -1,5 +1,3 @@ -import 'package:stream_chat/src/core/models/user.dart'; - /// Used to avoid to serialize properties to json // ignore: prefer_void_to_null Null readonly(_) => null; @@ -9,10 +7,6 @@ class Serialization { /// Used to avoid to serialize properties to json static const Function readOnly = readonly; - /// List of users to list of userIds - static List? userIds(List? users) => - users?.map((u) => u.id).toList(); - /// Takes unknown json keys and puts them in the `extra_data` key static Map moveToExtraDataFromRoot( Map json, diff --git a/packages/stream_chat/lib/src/core/utils.dart b/packages/stream_chat/lib/src/core/util/utils.dart similarity index 82% rename from packages/stream_chat/lib/src/core/utils.dart rename to packages/stream_chat/lib/src/core/util/utils.dart index c2d77927..c2204991 100644 --- a/packages/stream_chat/lib/src/core/utils.dart +++ b/packages/stream_chat/lib/src/core/util/utils.dart @@ -4,14 +4,14 @@ import 'dart:math' as math; // This alphabet uses `A-Za-z0-9_-` symbols. The genetic algorithm helped // optimize the gzip compression for this alphabet. const _alphabet = - 'ModuleSymbhasOwnPr0123456789ABCDEFGHNRVfgctiUvzKqYTJkLxpZXIjQW'; + 'ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW'; /// Generates a random String id /// Adopted from: https://github.com/ai/nanoid/blob/main/non-secure/index.js String randomId({int size = 21}) { var id = ''; for (var i = 0; i < size; i++) { - id += _alphabet[(math.Random().nextInt(32) * 64) | 0]; + id += _alphabet[(math.Random().nextDouble() * 64).floor() | 0]; } return id; } 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 d7e0f765..2470e403 100644 --- a/packages/stream_chat/lib/src/db/chat_persistence_client.dart +++ b/packages/stream_chat/lib/src/db/chat_persistence_client.dart @@ -8,7 +8,7 @@ import 'package:stream_chat/src/core/models/message.dart'; import 'package:stream_chat/src/core/models/reaction.dart'; import 'package:stream_chat/src/core/models/read.dart'; import 'package:stream_chat/src/core/models/user.dart'; -import 'package:stream_chat/src/extensions/iterable_extension.dart'; +import 'package:stream_chat/src/core/util/extension.dart'; /// A simple client used for persisting chat data locally. abstract class ChatPersistenceClient { diff --git a/packages/stream_chat/lib/src/extensions/iterable_extension.dart b/packages/stream_chat/lib/src/extensions/iterable_extension.dart deleted file mode 100644 index a7d30f76..00000000 --- a/packages/stream_chat/lib/src/extensions/iterable_extension.dart +++ /dev/null @@ -1,9 +0,0 @@ -/// Useful extension functions for [Iterable] -extension IterableX on Iterable { - /// Removes all the null values - /// and converts `Iterable` into `Iterable` - Iterable get withNullifyer => [ - for (final item in this) - if (item != null) item - ]; -} diff --git a/packages/stream_chat/lib/src/extensions/map_extension.dart b/packages/stream_chat/lib/src/extensions/map_extension.dart deleted file mode 100644 index d99ab96a..00000000 --- a/packages/stream_chat/lib/src/extensions/map_extension.dart +++ /dev/null @@ -1,6 +0,0 @@ -/// Useful extension functions for [Map] -extension MapX on Map { - /// Returns a new map with null keys or values removed - Map get nullProtected => - Map.from(this)..removeWhere((key, value) => key == null || value == null); -} diff --git a/packages/stream_chat/lib/src/extensions/rate_limit.dart b/packages/stream_chat/lib/src/extensions/rate_limit.dart deleted file mode 100644 index bf54fe5c..00000000 --- a/packages/stream_chat/lib/src/extensions/rate_limit.dart +++ /dev/null @@ -1,335 +0,0 @@ -// ignore_for_file: lines_longer_than_80_chars - -import 'dart:async' show Timer; -import 'dart:math' as math; - -/// Useful rate limiter extensions for [Function] class. -extension RateLimit on Function { - /// Converts this into a [Debounce] function. - Debounce debounced( - Duration wait, { - bool leading = false, - bool trailing = true, - Duration? maxWait, - }) => - Debounce( - this, - wait, - leading: leading, - trailing: trailing, - maxWait: maxWait, - ); - - /// Converts this into a [Throttle] function. - Throttle throttled( - Duration wait, { - bool leading = true, - bool trailing = true, - }) => - Throttle( - this, - wait, - leading: leading, - trailing: trailing, - ); -} - -/// TopLevel lambda to create [Debounce] functions. -Debounce debounce( - Function func, - Duration wait, { - bool leading = false, - bool trailing = true, - Duration? maxWait, -}) => - Debounce( - func, - wait, - leading: leading, - trailing: trailing, - maxWait: maxWait, - ); - -/// TopLevel lambda to create [Throttle] functions. -Throttle throttle( - Function func, - Duration wait, { - bool leading = true, - bool trailing = true, -}) => - Throttle( - func, - wait, - leading: leading, - trailing: trailing, - ); - -/// Creates a debounced function that delays invoking `func` until after `wait` -/// milliseconds have elapsed since the last time the debounced function was -/// invoked. The debounced function comes with a [Debounce.cancel] method to cancel -/// delayed `func` invocations and a [Debounce.flush] method to immediately invoke them. -/// Provide `leading` and/or `trailing` to indicate whether `func` should be -/// invoked on the `leading` and/or `trailing` edge of the `wait` interval. -/// The `func` is invoked with the last arguments provided to the [call] -/// function. Subsequent calls to the debounced function return the result of -/// the last `func` invocation. -/// -/// **Note:** If `leading` and `trailing` options are `true`, `func` is -/// invoked on the trailing edge of the timeout only if the debounced function -/// is invoked more than once during the `wait` timeout. -/// -/// If `wait` is [Duration.zero] and `leading` is `false`, -/// `func` invocation is deferred until the next tick. -/// -/// See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) -/// for details over the differences between [Debounce] and [Throttle]. -/// -/// Some examples: -/// -/// Avoid calling costly network calls when user is typing something. -/// ```dart -/// void fetchData(String query) async { -/// final data = api.getData(query); -/// doSomethingWithTheData(data); -/// } -/// -/// final debouncedFetchData = Debounce( -/// fetchData, -/// const Duration(milliseconds: 350), -/// ); -/// -/// void onSearchQueryChanged(query) { -/// debouncedFetchData(query); -/// } -/// ``` -/// -/// Cancel the trailing debounced invocation. -/// ```dart -/// void dispose() { -/// debounced.cancel(); -/// } -/// ``` -/// -/// Check for pending invocations. -/// ```dart -/// final status = debounced.isPending ? "Pending..." : "Ready"; -/// ``` -class Debounce { - /// Creates a new instance of [Debounce]. - Debounce( - this._func, - Duration wait, { - bool leading = false, - bool trailing = true, - Duration? maxWait, - }) : _leading = leading, - _trailing = trailing, - _wait = wait.inMilliseconds, - _maxing = maxWait != null { - if (_maxing) { - _maxWait = math.max(maxWait!.inMilliseconds, _wait); - } - } - - final Function _func; - final bool _leading; - final bool _trailing; - final int _wait; - final bool _maxing; - - late int _maxWait; - List? _lastArgs; - Map? _lastNamedArgs; - Timer? _timer; - int? _lastCallTime; - Object? _result; - int? _lastInvokeTime = 0; - - Object? _invokeFunc(int? time) { - final args = _lastArgs; - final namedArgs = _lastNamedArgs; - _lastArgs = _lastNamedArgs = null; - _lastInvokeTime = time; - return _result = Function.apply(_func, args, namedArgs); - } - - Timer _startTimer(Function pendingFunc, int wait) => - Timer(Duration(milliseconds: wait), pendingFunc as void Function()); - - bool _shouldInvoke(int time) { - final timeSinceLastCall = time - (_lastCallTime ?? double.nan); - final timeSinceLastInvoke = time - _lastInvokeTime!; - - // Either this is the first call, activity has stopped and we're at the - // trailing edge, the system time has gone backwards and we're treating - // it as the trailing edge, or we've hit the `maxWait` limit. - return _lastCallTime == null || - (timeSinceLastCall >= _wait) || - (timeSinceLastCall < 0) || - (_maxing && timeSinceLastInvoke >= _maxWait); - } - - Object? _trailingEdge(int time) { - _timer = null; - - // Only invoke if we have `lastArgs` which means `func` has been - // debounced at least once. - if (_trailing && _lastArgs != null) { - return _invokeFunc(time); - } - _lastArgs = _lastNamedArgs = null; - return _result; - } - - int _remainingWait(int time) { - final timeSinceLastCall = time - _lastCallTime!; - final timeSinceLastInvoke = time - _lastInvokeTime!; - final timeWaiting = _wait - timeSinceLastCall; - - return _maxing - ? math.min(timeWaiting, _maxWait - timeSinceLastInvoke) - : timeWaiting; - } - - void _timerExpired() { - final time = DateTime.now().millisecondsSinceEpoch; - if (_shouldInvoke(time)) { - _trailingEdge(time); - } else { - // Restart the timer. - _timer = _startTimer(_timerExpired, _remainingWait(time)); - } - } - - Object? _leadingEdge(int? time) { - // Reset any `maxWait` timer. - _lastInvokeTime = time; - // Start the timer for the trailing edge. - _timer = _startTimer(_timerExpired, _wait); - // Invoke the leading edge. - return _leading ? _invokeFunc(time) : _result; - } - - /// Cancels all the remaining delayed functions. - void cancel() { - _timer?.cancel(); - _lastInvokeTime = 0; - _lastArgs = _lastNamedArgs = _lastCallTime = _timer = null; - } - - /// Immediately invokes all the remaining delayed functions. - Object? flush() { - final now = DateTime.now().millisecondsSinceEpoch; - return _timer == null ? _result : _trailingEdge(now); - } - - /// True if there are functions remaining to get invoked. - bool get isPending => _timer != null; - - /// Calls/invokes this class like a function. - /// Pass [args] and [namedArgs] to be used while invoking [_func]. - Object? call( - List args, { - Map? namedArgs, - }) { - final time = DateTime.now().millisecondsSinceEpoch; - final isInvoking = _shouldInvoke(time); - - _lastArgs = args; - _lastNamedArgs = namedArgs as Map?; - _lastCallTime = time; - - if (isInvoking) { - if (_timer == null) { - return _leadingEdge(_lastCallTime); - } - if (_maxing) { - // Handle invocations in a tight loop. - _timer = _startTimer(_timerExpired, _wait); - return _invokeFunc(_lastCallTime); - } - } - _timer ??= _startTimer(_timerExpired, _wait); - return _result; - } -} - -/// Creates a throttled function that only invokes `func` at most once per -/// every `wait` milliseconds. The throttled function comes with a [Throttle.cancel] -/// method to cancel delayed `func` invocations and a [Throttle.flush] method to -/// immediately invoke them. Provide `leading` and/or `trailing` to indicate -/// whether `func` should be invoked on the `leading` and/or `trailing` edge of the `wait` timeout. -/// The `func` is invoked with the last arguments provided to the -/// throttled function. Subsequent calls to the throttled function return the -/// result of the last `func` invocation. -/// -/// **Note:** If `leading` and `trailing` options are `true`, `func` is -/// invoked on the trailing edge of the timeout only if the throttled function -/// is invoked more than once during the `wait` timeout. -/// -/// If `wait` is [Duration.zero] and `leading` is `false`, `func` invocation is deferred -/// until the next tick. -/// -/// See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) -/// for details over the differences between [Throttle] and [Debounce]. -/// -/// Some examples: -/// -/// Avoid excessively rebuilding UI progress while uploading data to server. -/// ```dart -/// void updateUI(Data data) { -/// updateProgress(data); -/// } -/// -/// final throttledUpdateUI = Throttle( -/// updateUI, -/// const Duration(milliseconds: 350), -/// ); -/// -/// void onUploadProgressChanged(progress) { -/// throttledUpdateUI(progress); -/// } -/// ``` -/// -/// Cancel the trailing throttled invocation. -/// ```dart -/// void dispose() { -/// throttled.cancel(); -/// } -/// ``` -/// -/// Check for pending invocations. -/// ```dart -/// final status = throttled.isPending ? "Pending..." : "Ready"; -/// ``` -class Throttle { - /// Creates a new instance of [Throttle] - Throttle( - Function func, - Duration wait, { - bool leading = true, - bool trailing = true, - }) : _debounce = Debounce( - func, - wait, - leading: leading, - trailing: trailing, - maxWait: wait, - ); - - final Debounce _debounce; - - /// Cancels all the remaining delayed functions. - void cancel() => _debounce.cancel(); - - /// Immediately invokes all the remaining delayed functions. - Object? flush() => _debounce.flush(); - - /// True if there are functions remaining to get invoked. - bool get isPending => _debounce.isPending; - - /// Calls/invokes this class like a function. - /// Pass [args] and [namedArgs] to be used while invoking `func`. - Object? call(List args, {Map? namedArgs}) => - _debounce.call(args, namedArgs: namedArgs); -} diff --git a/packages/stream_chat/lib/src/extensions/string_extension.dart b/packages/stream_chat/lib/src/extensions/string_extension.dart deleted file mode 100644 index 440e2cb6..00000000 --- a/packages/stream_chat/lib/src/extensions/string_extension.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:http_parser/http_parser.dart' as http_parser; -import 'package:mime/mime.dart'; - -/// Useful extension functions for [String] -extension StringX on String { - /// Returns the mime type from the passed file name. - http_parser.MediaType? get mimeType { - if (toLowerCase().endsWith('heic')) { - return http_parser.MediaType.parse('image/heic'); - } else { - final mimeType = lookupMimeType(this); - if (mimeType == null) { - return null; - } - return http_parser.MediaType.parse(mimeType); - } - } -} diff --git a/packages/stream_chat/lib/src/ws/websocket.dart b/packages/stream_chat/lib/src/ws/websocket.dart index 6c36fd0b..4c7c91ed 100644 --- a/packages/stream_chat/lib/src/ws/websocket.dart +++ b/packages/stream_chat/lib/src/ws/websocket.dart @@ -5,8 +5,7 @@ import 'dart:math' as math; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat/src/errors/chat_error_code.dart'; -import 'package:stream_chat/src/errors/stream_chat_error.dart'; +import 'package:stream_chat/src/core/error/error.dart'; import 'package:stream_chat/src/ws/connection_status.dart'; import 'package:stream_chat/src/core/http/token.dart'; import 'package:stream_chat/src/core/http/token_manager.dart'; diff --git a/packages/stream_chat/lib/stream_chat.dart b/packages/stream_chat/lib/stream_chat.dart index 66dace1d..3c2e3d21 100644 --- a/packages/stream_chat/lib/stream_chat.dart +++ b/packages/stream_chat/lib/stream_chat.dart @@ -6,14 +6,14 @@ export 'package:dio/src/multipart_file.dart'; export 'package:dio/src/options.dart'; export 'package:dio/src/options.dart' show ProgressCallback; export 'package:logging/logging.dart' show Logger, Level; +export 'package:rate_limiter/rate_limiter.dart'; -export './src/api/channel.dart'; -export './src/client.dart'; export './src/core/api/attachment_file_uploader.dart' show AttachmentFileUploader; export './src/core/api/requests.dart'; export './src/core/api/requests.dart'; export './src/core/api/responses.dart'; +export './src/core/error/error.dart'; export './src/core/models/action.dart'; export './src/core/models/attachment.dart'; export './src/core/models/attachment_file.dart'; @@ -31,11 +31,10 @@ export './src/core/models/own_user.dart'; export './src/core/models/reaction.dart'; export './src/core/models/read.dart'; export './src/core/models/user.dart'; +export './src/core/util/extension.dart'; export './src/db/chat_persistence_client.dart'; -export './src/errors/chat_error_code.dart'; -export './src/errors/stream_chat_error.dart'; export './src/event_type.dart'; -export './src/extensions/rate_limit.dart'; -export './src/extensions/string_extension.dart'; export './src/location.dart'; export './src/ws/connection_status.dart'; +export 'src/client/channel.dart'; +export 'src/client/client.dart'; diff --git a/packages/stream_chat/lib/version.dart b/packages/stream_chat/lib/version.dart index 6c68818a..6bf106a2 100644 --- a/packages/stream_chat/lib/version.dart +++ b/packages/stream_chat/lib/version.dart @@ -1,4 +1,4 @@ -import 'package:stream_chat/src/client.dart'; +import 'package:stream_chat/src/client/client.dart'; /// Current package version /// Used in [StreamChatClient] to build the `x-stream-client` header diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index 23f6c114..f4554959 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: logging: ^1.0.1 meta: ^1.3.0 mime: ^1.0.0 + rate_limiter: ^0.1.1 rxdart: ^0.27.0 uuid: ^3.0.4 web_socket_channel: ^2.0.0 diff --git a/packages/stream_chat/test/version_test.dart b/packages/stream_chat/test/version_test.dart index 8e7112a4..29076db0 100644 --- a/packages/stream_chat/test/version_test.dart +++ b/packages/stream_chat/test/version_test.dart @@ -4,6 +4,23 @@ import 'dart:io'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/version.dart'; import 'package:test/test.dart'; +import 'dart:math' as math; + +// This alphabet uses `A-Za-z0-9_-` symbols. The genetic algorithm helped +// optimize the gzip compression for this alphabet. +const _alphabet = + 'ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW'; + +/// Generates a random String id +/// Adopted from: https://github.com/ai/nanoid/blob/main/non-secure/index.js +String randomId({int size = 21}) { + var id = ''; + for (var i = 0; i < size; i++) { + id += _alphabet[(math.Random().nextDouble() * 64).floor() | 0]; + } + return id; +} + void prepareTest() { // https://github.com/flutter/flutter/issues/20907 @@ -15,30 +32,32 @@ void prepareTest() { void main() { prepareTest(); test('stream chat version matches pubspec', () async { - /// Create a new instance of [StreamChatClient] - /// by passing the apikey obtained from your project dashboard. - final client = StreamChatClient('b67pax5b2wdq', logLevel: Level.INFO); + print(randomId()); - /// Set the current user. In a production scenario, this should be done using - /// a backend to generate a user token using our server SDK. - /// Please see the following for more information: - /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ - await client.connectUser( - User( - id: 'cool-shadow-7', - extraData: { - 'image': - 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', - }, - ), - '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo''', - ); - - try { - await client.banUser('asdasdas'); - } catch (e) { - print(e); - } + // /// Create a new instance of [StreamChatClient] + // /// by passing the apikey obtained from your project dashboard. + // final client = StreamChatClient('b67pax5b2wdq', logLevel: Level.INFO); + // + // /// Set the current user. In a production scenario, this should be done using + // /// a backend to generate a user token using our server SDK. + // /// Please see the following for more information: + // /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ + // await client.connectUser( + // User( + // id: 'cool-shadow-7', + // extraData: { + // 'image': + // 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', + // }, + // ), + // '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo''', + // ); + // + // try { + // await client.banUser('asdasdas'); + // } catch (e) { + // print(e); + // } // final pubspecPath = '${Directory.current.path}/pubspec.yaml'; // final pubspec = File(pubspecPath).readAsStringSync();