Merge branches 'develop' and 'ref/segregate-api-layer' of https://github.com/GetStream/stream-chat-flutter into ref/segregate-api-layer

 Conflicts:
	packages/stream_chat/lib/src/api/channel.dart
	packages/stream_chat/lib/src/client.dart
	packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart
This commit is contained in:
Sahil Kumar
2021-05-27 20:07:11 +05:30
44 changed files with 753 additions and 188 deletions
+6
View File
@@ -1,3 +1,9 @@
## 2.0.0-nullsafety.2
- Added new `Filter.raw` constructor
- Changed extraData
- Minor fixes
## 2.0.0-nullsafety.1 ## 2.0.0-nullsafety.1
- Migrate this package to null safety - Migrate this package to null safety
@@ -1327,7 +1327,7 @@ class ChannelClientState {
/// This flag should be managed by UI sdks. /// This flag should be managed by UI sdks.
/// When false, any new message (received by WebSocket event /// When false, any new message (received by WebSocket event
/// - [EventType.messageNew]) will not be pushed on to message list. /// - [EventType.messageNew]) will not be pushed on to message list.
bool get isUpToDate => _isUpToDateController.value ?? true; bool get isUpToDate => _isUpToDateController.value;
set isUpToDate(bool isUpToDate) => _isUpToDateController.add(isUpToDate); set isUpToDate(bool isUpToDate) => _isUpToDateController.add(isUpToDate);
@@ -1428,7 +1428,7 @@ class ChannelClientState {
} }
if (_countMessageAsUnread(message)) { if (_countMessageAsUnread(message)) {
_unreadCountController.add(_unreadCountController.value! + 1); _unreadCountController.add(_unreadCountController.value + 1);
} }
})); }));
} }
@@ -1666,13 +1666,13 @@ class ChannelClientState {
a.createdAt.compareTo(b.createdAt); a.createdAt.compareTo(b.createdAt);
/// The channel state related to this client /// The channel state related to this client
ChannelState get _channelState => _channelStateController.value!; ChannelState get _channelState => _channelStateController.value;
/// The channel state related to this client as a stream /// The channel state related to this client as a stream
Stream<ChannelState> get channelStateStream => _channelStateController.stream; Stream<ChannelState> get channelStateStream => _channelStateController.stream;
/// The channel state related to this client /// The channel state related to this client
ChannelState get channelState => _channelStateController.value!; ChannelState get channelState => _channelStateController.value;
late BehaviorSubject<ChannelState> _channelStateController; late BehaviorSubject<ChannelState> _channelStateController;
final Debounce _debouncedUpdatePersistenceChannelState; final Debounce _debouncedUpdatePersistenceChannelState;
@@ -1684,7 +1684,7 @@ class ChannelClientState {
/// The channel threads related to this channel /// The channel threads related to this channel
Map<String, List<Message>> get threads => Map<String, List<Message>> get threads =>
_threadsController.value!.map((key, value) => MapEntry(key, value)); _threadsController.value.map((key, value) => MapEntry(key, value));
/// The channel threads related to this channel as a stream /// The channel threads related to this channel as a stream
Stream<Map<String, List<Message>>> get threadsStream => Stream<Map<String, List<Message>>> get threadsStream =>
@@ -1701,7 +1701,7 @@ class ChannelClientState {
} }
/// Channel related typing users last value /// Channel related typing users last value
List<User> get typingEvents => _typingEventsController.value!; List<User> get typingEvents => _typingEventsController.value;
/// Channel related typing users stream /// Channel related typing users stream
Stream<List<User>> get typingEventsStream => _typingEventsController.stream; Stream<List<User>> get typingEventsStream => _typingEventsController.stream;
+9 -6
View File
@@ -1248,7 +1248,10 @@ class StreamChatClient {
); );
} }
return updateMessage( return updateMessage(
message.copyWith(pinned: true, pinExpires: pinExpires), message.copyWith(
pinned: true,
pinExpires: pinExpires,
),
); );
} }
@@ -1388,25 +1391,25 @@ class ClientState {
void updateUser(User? user) => updateUsers([user]); void updateUser(User? user) => updateUsers([user]);
/// The current user /// The current user
OwnUser? get user => _userController.value; OwnUser? get user => _userController.valueOrNull;
/// The current user as a stream /// The current user as a stream
Stream<OwnUser?> get userStream => _userController.stream; Stream<OwnUser?> get userStream => _userController.stream;
/// The current user /// The current user
Map<String, User> get users => _usersController.value!; Map<String, User> get users => _usersController.value;
/// The current user as a stream /// The current user as a stream
Stream<Map<String, User>> get usersStream => _usersController.stream; Stream<Map<String, User>> get usersStream => _usersController.stream;
/// The current unread channels count /// The current unread channels count
int? get unreadChannels => _unreadChannelsController.value; int? get unreadChannels => _unreadChannelsController.valueOrNull;
/// The current unread channels count as a stream /// The current unread channels count as a stream
Stream<int?> get unreadChannelsStream => _unreadChannelsController.stream; Stream<int?> get unreadChannelsStream => _unreadChannelsController.stream;
/// The current total unread messages count /// The current total unread messages count
int? get totalUnreadCount => _totalUnreadCountController.value; int? get totalUnreadCount => _totalUnreadCountController.valueOrNull;
/// The current total unread messages count as a stream /// The current total unread messages count as a stream
Stream<int?> get totalUnreadCountStream => _totalUnreadCountController.stream; Stream<int?> get totalUnreadCountStream => _totalUnreadCountController.stream;
@@ -1415,7 +1418,7 @@ class ClientState {
Stream<Map<String, Channel>> get channelsStream => _channelsController.stream; Stream<Map<String, Channel>> get channelsStream => _channelsController.stream;
/// The current list of channels in memory /// The current list of channels in memory
Map<String, Channel> get channels => _channelsController.value!; Map<String, Channel> get channels => _channelsController.value;
set channels(Map<String, Channel> channelMap) { set channels(Map<String, Channel> channelMap) {
final newChannels = {...channels, ...channelMap}; final newChannels = {...channels, ...channelMap};
+1 -1
View File
@@ -3,4 +3,4 @@ import 'package:stream_chat/src/client.dart';
/// Current package version /// Current package version
/// Used in [StreamChatClient] to build the `x-stream-client` header /// Used in [StreamChatClient] to build the `x-stream-client` header
// ignore: constant_identifier_names // ignore: constant_identifier_names
const PACKAGE_VERSION = '2.0.0-nullsafety.1'; const PACKAGE_VERSION = '2.0.0-nullsafety.2';
+2 -2
View File
@@ -1,7 +1,7 @@
name: stream_chat name: stream_chat
homepage: https://getstream.io/ homepage: https://getstream.io/
description: The official Dart client for Stream Chat, a service for building chat applications. description: The official Dart client for Stream Chat, a service for building chat applications.
version: 2.0.0-nullsafety.1 version: 2.0.0-nullsafety.2
repository: https://github.com/GetStream/stream-chat-flutter repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
@@ -20,7 +20,7 @@ dependencies:
logging: ^1.0.1 logging: ^1.0.1
meta: ^1.3.0 meta: ^1.3.0
mime: ^1.0.0 mime: ^1.0.0
rxdart: ^0.26.0 rxdart: ^0.27.0
uuid: ^3.0.4 uuid: ^3.0.4
web_socket_channel: ^2.0.0 web_socket_channel: ^2.0.0
@@ -449,6 +449,51 @@ void main() {
.called(1); .called(1);
}); });
test('should be pinned successfully with null timeout', () async {
final mockDio = MockDio();
when(() => mockDio.options).thenReturn(BaseOptions());
when(() => mockDio.interceptors).thenReturn(Interceptors());
final client = StreamChatClient(
'api-key',
httpClient: mockDio,
tokenProvider: (_) async => '',
);
final channelClient = client.channel('messaging', id: 'testid');
final message = Message(text: 'Hello', id: 'test');
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: jsonEncode(ChannelState()),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
when(
() => mockDio.post<String>(
'/messages/${message.id}',
data: anything,
),
).thenAnswer(
(_) async => Response(
data: jsonEncode({'message': message}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
);
await channelClient.pinMessage(message);
verify(() =>
mockDio.post<String>('/messages/${message.id}', data: anything))
.called(1);
});
test('should be unpinned successfully', () async { test('should be unpinned successfully', () async {
final mockDio = MockDio(); final mockDio = MockDio();
@@ -1112,6 +1112,28 @@ void main() {
data: {'message': anything})).called(1); data: {'message': anything})).called(1);
}); });
test('should complete successfully with a null value', () async {
final message = Message(text: 'Hello');
when(
() => mockDio.post<String>(
'/messages/${message.id}',
data: anything,
),
).thenAnswer(
(_) async => Response(
data: jsonEncode({'message': message}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
);
await client.pinMessage(message);
verify(() => mockDio.post<String>('/messages/${message.id}',
data: {'message': anything})).called(1);
});
test('should unpin message successfully', () async { test('should unpin message successfully', () async {
final message = Message(text: 'Hello'); final message = Message(text: 'Hello');
@@ -1,3 +1,9 @@
## 2.0.0-nullsafety.4
- Minor fixes and improvements
- Updated `stream_chat_core` dependency
- Improved performance of `MessageWidget` component
## 2.0.0-nullsafety.3 ## 2.0.0-nullsafety.3
- Fix MessageInput overflow when there are no actions - Fix MessageInput overflow when there are no actions
@@ -0,0 +1,150 @@
analyzer:
enable-experiment:
- extension-methods
exclude:
- lib/**/*.g.dart
- example/**
- lib/src/emoji
- lib/**/*.freezed.dart
- test/**
linter:
rules:
# these rules are documented on and in the same order as
# the Dart Lint rules page to make maintenance easier
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
- always_use_package_imports
- avoid_empty_else
- avoid_relative_lib_imports
- avoid_slow_async_io
- avoid_types_as_parameter_names
- cancel_subscriptions
- close_sinks
- control_flow_in_finally
- empty_statements
- hash_and_equals
- invariant_booleans
- iterable_contains_unrelated_type
- list_remove_unrelated_type
- literal_only_boolean_expressions
- no_adjacent_strings_in_list
- no_duplicate_case_values
- no_logic_in_create_state
- prefer_void_to_null
- test_types_in_equals
- throw_in_finally
- unnecessary_statements
- unrelated_type_equality_checks
- omit_local_variable_types
- use_key_in_widget_constructors
- valid_regexps
- always_declare_return_types
- always_require_non_null_named_parameters
- annotate_overrides
- avoid_bool_literals_in_conditional_expressions
- avoid_catching_errors
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
- avoid_positional_boolean_parameters
- avoid_private_typedef_functions
- avoid_redundant_argument_values
- avoid_return_types_on_setters
- avoid_returning_null_for_void
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_unnecessary_containers
- avoid_unused_constructor_parameters
- await_only_futures
- camel_case_extensions
- camel_case_types
- cascade_invocations
- constant_identifier_names
- curly_braces_in_flow_control_structures
- directives_ordering
- empty_catches
- empty_constructor_bodies
- exhaustive_cases
- file_names
- implementation_imports
- join_return_with_assignment
- leading_newlines_in_multiline_strings
- library_names
- library_prefixes
- lines_longer_than_80_chars
- missing_whitespace_between_adjacent_strings
- non_constant_identifier_names
- null_closures
- one_member_abstracts
- only_throw_errors
- package_api_docs
- package_prefixed_library_names
- parameter_assignments
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_asserts_with_message
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_constructors_over_static_methods
- prefer_contains
- prefer_equal_for_default_values
- prefer_expression_function_bodies
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
- prefer_int_literals
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_null_aware_operators
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- provide_deprecation_message
- public_member_api_docs
- recursive_getters
- sized_box_for_whitespace
- slash_for_doc_comments
- sort_child_properties_last
- sort_constructors_first
- sort_unnamed_constructors_first
- type_annotate_public_apis
- type_init_formals
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
- unnecessary_lambdas
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_parenthesis
- unnecessary_raw_strings
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- use_is_even_rather_than_modulo
- use_late_for_private_fields_and_variables
- use_rethrow_when_possible
- use_setters_to_change_properties
- use_to_and_as_if_applicable
- package_names
- sort_pub_dependencies
- cast_nullable_to_non_nullable
- unnecessary_null_checks
- tighten_type_of_initializing_formals
- null_check_on_nullable_type_parameter
@@ -159,6 +159,7 @@ class FileAttachment extends AttachmentWidget {
shape: _getDefaultShape(context), shape: _getDefaultShape(context),
child: source.when( child: source.when(
local: () => VideoThumbnailImage( local: () => VideoThumbnailImage(
fit: BoxFit.cover,
video: attachment.file!.path!, video: attachment.file!.path!,
placeholderBuilder: (_) => const Center( placeholderBuilder: (_) => const Center(
child: SizedBox( child: SizedBox(
@@ -169,6 +170,7 @@ class FileAttachment extends AttachmentWidget {
), ),
), ),
network: () => VideoThumbnailImage( network: () => VideoThumbnailImage(
fit: BoxFit.cover,
video: attachment.assetUrl!, video: attachment.assetUrl!,
placeholderBuilder: (_) => const Center( placeholderBuilder: (_) => const Center(
child: SizedBox( child: SizedBox(
@@ -212,50 +214,42 @@ class FileAttachment extends AttachmentWidget {
final attachmentId = attachment.id; final attachmentId = attachment.id;
var trailingWidget = trailing; var trailingWidget = trailing;
trailingWidget ??= attachment.uploadState.when( trailingWidget ??= attachment.uploadState.when(
preparing: () => Padding( preparing: () => Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: _buildButton( child: _buildButton(
icon: StreamSvgIcon.close(color: theme.colorTheme.white), icon: StreamSvgIcon.close(color: theme.colorTheme.white),
fillColor: theme.colorTheme.overlayDark, fillColor: theme.colorTheme.overlayDark,
onPressed: () => channel.cancelAttachmentUpload(attachmentId), onPressed: () => channel.cancelAttachmentUpload(attachmentId),
), ),
),
inProgress: (_, __) => Padding(
padding: const EdgeInsets.all(8),
child: _buildButton(
icon: StreamSvgIcon.close(color: theme.colorTheme.white),
fillColor: theme.colorTheme.overlayDark,
onPressed: () => channel.cancelAttachmentUpload(attachmentId),
),
),
success: () => Padding(
padding: const EdgeInsets.all(8),
child: CircleAvatar(
backgroundColor: theme.colorTheme.accentBlue,
maxRadius: 12,
child: StreamSvgIcon.check(color: theme.colorTheme.white),
),
),
failed: (_) => Padding(
padding: const EdgeInsets.all(8),
child: _buildButton(
icon: StreamSvgIcon.retry(color: theme.colorTheme.white),
fillColor: theme.colorTheme.overlayDark,
onPressed: () => channel.retryAttachmentUpload(
message.id,
attachmentId,
), ),
inProgress: (_, __) => Padding( ),
padding: const EdgeInsets.all(8), ),
child: _buildButton( );
icon: StreamSvgIcon.close(color: theme.colorTheme.white),
fillColor: theme.colorTheme.overlayDark,
onPressed: () => channel.cancelAttachmentUpload(attachmentId),
),
),
success: () => Padding(
padding: const EdgeInsets.all(8),
child: CircleAvatar(
backgroundColor: theme.colorTheme.accentBlue,
maxRadius: 12,
child: StreamSvgIcon.check(color: theme.colorTheme.white),
),
),
failed: (_) => Padding(
padding: const EdgeInsets.all(8),
child: _buildButton(
icon: StreamSvgIcon.retry(color: theme.colorTheme.white),
fillColor: theme.colorTheme.overlayDark,
onPressed: () => channel.retryAttachmentUpload(
message.id,
attachmentId,
),
),
),
) ??
IconButton(
icon: StreamSvgIcon.cloudDownload(color: theme.colorTheme.black),
visualDensity: VisualDensity.compact,
splashRadius: 16,
onPressed: () {
launchURL(context, attachment.assetUrl);
},
);
if (message.status == MessageSendingStatus.sent) { if (message.status == MessageSendingStatus.sent) {
trailingWidget = IconButton( trailingWidget = IconButton(
@@ -281,25 +275,17 @@ class FileAttachment extends AttachmentWidget {
color: theme.colorTheme.grey, color: theme.colorTheme.grey,
); );
return attachment.uploadState.when( return attachment.uploadState.when(
preparing: () => UploadProgressIndicator( preparing: () => Text(fileSize(size), style: textStyle),
uploaded: 0, inProgress: (sent, total) => UploadProgressIndicator(
total: double.maxFinite.toInt(), uploaded: sent,
showBackground: false, total: total,
padding: EdgeInsets.zero, showBackground: false,
textStyle: textStyle, padding: EdgeInsets.zero,
progressIndicatorColor: theme.colorTheme.accentBlue, textStyle: textStyle,
), progressIndicatorColor: theme.colorTheme.accentBlue,
inProgress: (sent, total) => UploadProgressIndicator( ),
uploaded: sent, success: () => Text(fileSize(size), style: textStyle),
total: total, failed: (_) => Text('UPLOAD ERROR', style: textStyle),
showBackground: false, );
padding: EdgeInsets.zero,
textStyle: textStyle,
progressIndicatorColor: theme.colorTheme.accentBlue,
),
success: () => Text(fileSize(size), style: textStyle),
failed: (_) => Text('UPLOAD ERROR', style: textStyle),
) ??
Text(fileSize(size), style: textStyle);
} }
} }
@@ -137,6 +137,7 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
showMessage: showConnectionStateTile ? showStatus : false, showMessage: showConnectionStateTile ? showStatus : false,
message: statusString, message: statusString,
child: AppBar( child: AppBar(
textTheme: Theme.of(context).textTheme,
brightness: Theme.of(context).brightness, brightness: Theme.of(context).brightness,
elevation: 1, elevation: 1,
leading: leadingWidget, leading: leadingWidget,
@@ -120,6 +120,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
showMessage: showConnectionStateTile ? showStatus : false, showMessage: showConnectionStateTile ? showStatus : false,
message: statusString, message: statusString,
child: AppBar( child: AppBar(
textTheme: Theme.of(context).textTheme,
brightness: Theme.of(context).brightness, brightness: Theme.of(context).brightness,
elevation: 1, elevation: 1,
backgroundColor: chatThemeData.channelListHeaderTheme.color, backgroundColor: chatThemeData.channelListHeaderTheme.color,
@@ -29,8 +29,11 @@ class ChannelName extends StatelessWidget {
return StreamBuilder<Map<String, dynamic>>( return StreamBuilder<Map<String, dynamic>>(
stream: channel.extraDataStream, stream: channel.extraDataStream,
initialData: channel.extraData, initialData: channel.extraData,
builder: (context, snapshot) => builder: (context, snapshot) => _buildName(
_buildName(snapshot.data!, channel.state?.members, client), snapshot.data!,
channel.state?.members,
client,
),
); );
} }
@@ -76,7 +76,7 @@ class _ImageFooterState extends State<ImageFooter> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final showShareButton = !kIsWeb; const showShareButton = !kIsWeb;
final mediaQueryData = MediaQuery.of(context); final mediaQueryData = MediaQuery.of(context);
final chatThemeData = StreamChatTheme.of(context); final chatThemeData = StreamChatTheme.of(context);
return SizedBox.fromSize( return SizedBox.fromSize(
@@ -53,6 +53,7 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final chatThemeData = StreamChatTheme.of(context); final chatThemeData = StreamChatTheme.of(context);
return AppBar( return AppBar(
textTheme: Theme.of(context).textTheme,
brightness: Theme.of(context).brightness, brightness: Theme.of(context).brightness,
elevation: 1, elevation: 1,
leading: showBackButton leading: showBackButton
@@ -76,7 +76,7 @@ enum SendButtonLocation {
const _kMinMediaPickerSize = 360.0; const _kMinMediaPickerSize = 360.0;
const _kMaxAttachmentSize = 20971520; // 20MB in Bytes const _kDefaultMaxAttachmentSize = 20971520; // 20MB in Bytes
/// Inactive state /// Inactive state
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input.png)
@@ -148,11 +148,17 @@ class MessageInput extends StatefulWidget {
this.activeSendButton, this.activeSendButton,
this.showCommandsButton = true, this.showCommandsButton = true,
this.mentionsTileBuilder, this.mentionsTileBuilder,
this.maxAttachmentSize = _kDefaultMaxAttachmentSize,
}) : super(key: key); }) : super(key: key);
/// Message to edit /// Message to edit
final Message? editMessage; final Message? editMessage;
/// Max attachment size in bytes
/// Defaults to 20 MB
/// do not set it if you're using our default CDN
final int maxAttachmentSize;
/// Message to start with /// Message to start with
final Message? initialMessage; final Message? initialMessage;
@@ -1108,12 +1114,12 @@ class MessageInputState extends State<MessageInput> {
bytes: mediaFile.readAsBytesSync(), bytes: mediaFile.readAsBytesSync(),
); );
if (file.size! > _kMaxAttachmentSize) { if (file.size! > widget.maxAttachmentSize) {
if (medium.type == AssetType.video) { if (medium.type == AssetType.video && file.path != null) {
final mediaInfo = await (VideoService.compressVideo(file.path) final mediaInfo = await (VideoService.compressVideo(file.path!)
as FutureOr<MediaInfo>); as FutureOr<MediaInfo>);
if (mediaInfo.filesize! > _kMaxAttachmentSize) { if (mediaInfo.filesize! > widget.maxAttachmentSize) {
_showErrorAlert( _showErrorAlert(
// ignore: lines_longer_than_80_chars // ignore: lines_longer_than_80_chars
'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.', 'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.',
@@ -1473,16 +1479,12 @@ class MessageInputState extends State<MessageInput> {
final containsUrl = widget.quotedMessage!.attachments final containsUrl = widget.quotedMessage!.attachments
.any((element) => element.ogScrapeUrl != null) == .any((element) => element.ogScrapeUrl != null) ==
true; true;
return Transform( return QuotedMessageWidget(
transform: Matrix4.rotationY(pi), reverse: true,
alignment: Alignment.center, showBorder: !containsUrl,
child: QuotedMessageWidget( message: widget.quotedMessage!,
reverse: true, messageTheme: StreamChatTheme.of(context).otherMessageTheme,
showBorder: !containsUrl, padding: const EdgeInsets.fromLTRB(8, 8, 8, 0),
message: widget.quotedMessage!,
messageTheme: StreamChatTheme.of(context).otherMessageTheme,
padding: const EdgeInsets.fromLTRB(8, 8, 8, 0),
),
); );
} }
@@ -1509,7 +1511,9 @@ class MessageInputState extends State<MessageInput> {
(e) => ClipRRect( (e) => ClipRRect(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
child: FileAttachment( child: FileAttachment(
message: Message(), // dummy message message: Message(
status: MessageSendingStatus.sending,
), // dummy message
attachment: e, attachment: e,
size: Size( size: Size(
MediaQuery.of(context).size.width * 0.65, MediaQuery.of(context).size.width * 0.65,
@@ -1897,15 +1901,16 @@ class MessageInputState extends State<MessageInput> {
final attachment = Attachment( final attachment = Attachment(
file: file, file: file,
type: attachmentType, type: attachmentType,
uploadState: const UploadState.preparing(),
extraData: extraDataMap, extraData: extraDataMap,
); );
if (file.size! > _kMaxAttachmentSize) { if (file.size! > widget.maxAttachmentSize) {
if (attachmentType == 'Video') { if (attachmentType == 'video' && file.path != null) {
final mediaInfo = await (VideoService.compressVideo(file.path) final mediaInfo = await (VideoService.compressVideo(file.path!)
as FutureOr<MediaInfo>); as FutureOr<MediaInfo>);
if (mediaInfo.filesize! > _kMaxAttachmentSize) { if (mediaInfo.filesize! > widget.maxAttachmentSize) {
_showErrorAlert( _showErrorAlert(
// ignore: lines_longer_than_80_chars // ignore: lines_longer_than_80_chars
'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.', 'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.',
@@ -570,9 +570,9 @@ class _MessageListViewState extends State<MessageListView> {
return const SizedBox(); return const SizedBox();
} }
var index = _getTopElement(values).index; var index = _getTopElement(values)?.index;
if (index > messages.length) { if (index == null || index > messages.length) {
return const SizedBox(); return const SizedBox();
} }
@@ -598,10 +598,17 @@ class _MessageListViewState extends State<MessageListView> {
StreamChannelState? channel, QueryDirection direction) => StreamChannelState? channel, QueryDirection direction) =>
_messageListController.paginateData!(direction: direction); _messageListController.paginateData!(direction: direction);
ItemPosition _getTopElement(Iterable<ItemPosition> values) => values ItemPosition? _getTopElement(Iterable<ItemPosition> values) {
.where((ItemPosition position) => position.itemLeadingEdge < 0.9) final inView =
.reduce((ItemPosition max, ItemPosition position) => values.where((ItemPosition position) => position.itemLeadingEdge < 0.9);
position.itemLeadingEdge > max.itemLeadingEdge ? position : max);
if (inView.isEmpty) {
return null;
}
return inView.reduce((ItemPosition max, ItemPosition position) =>
position.itemLeadingEdge > max.itemLeadingEdge ? position : max);
}
Widget _buildScrollToBottom() => StreamBuilder<Tuple2<bool, int>>( Widget _buildScrollToBottom() => StreamBuilder<Tuple2<bool, int>>(
stream: Rx.combineLatest2( stream: Rx.combineLatest2(
@@ -815,11 +822,13 @@ class _MessageListViewState extends State<MessageListView> {
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
showSendingIndicator: false, showSendingIndicator: false,
onThreadTap: _onThreadTap, onThreadTap: _onThreadTap,
borderRadiusGeometry: const BorderRadius.only( borderRadiusGeometry: BorderRadius.only(
topLeft: Radius.circular(16), topLeft: const Radius.circular(16),
bottomLeft: Radius.circular(2), bottomLeft:
topRight: Radius.circular(16), isMyMessage ? const Radius.circular(16) : const Radius.circular(2),
bottomRight: Radius.circular(16), topRight: const Radius.circular(16),
bottomRight:
isMyMessage ? const Radius.circular(2) : const Radius.circular(16),
), ),
textPadding: EdgeInsets.symmetric( textPadding: EdgeInsets.symmetric(
vertical: 8, vertical: 8,
@@ -102,21 +102,24 @@ class QuotedMessageWidget extends StatelessWidget {
bool get _containsText => message.text?.isNotEmpty == true; bool get _containsText => message.text?.isNotEmpty == true;
@override @override
Widget build(BuildContext context) => Padding( Widget build(BuildContext context) {
padding: padding, final children = [
child: InkWell( Flexible(child: _buildMessage(context)),
onTap: onTap, const SizedBox(width: 8),
child: Row( if (message.user != null) _buildUserAvatar(),
crossAxisAlignment: CrossAxisAlignment.end, ];
mainAxisSize: MainAxisSize.min, return Padding(
children: [ padding: padding,
Flexible(child: _buildMessage(context)), child: InkWell(
const SizedBox(width: 8), onTap: onTap,
if (message.user != null) _buildUserAvatar(), child: Row(
], crossAxisAlignment: CrossAxisAlignment.end,
), mainAxisSize: MainAxisSize.min,
children: reverse ? children.reversed.toList() : children,
), ),
); ),
);
}
Widget _buildMessage(BuildContext context) { Widget _buildMessage(BuildContext context) {
final isOnlyEmoji = message.text!.isOnlyEmoji; final isOnlyEmoji = message.text!.isOnlyEmoji;
@@ -154,10 +157,11 @@ class QuotedMessageWidget extends StatelessWidget {
color: StreamChatTheme.of(context).colorTheme.greyGainsboro, color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
) )
: null, : null,
borderRadius: const BorderRadius.only( borderRadius: BorderRadius.only(
topRight: Radius.circular(12), topRight: const Radius.circular(12),
topLeft: Radius.circular(12), topLeft: const Radius.circular(12),
bottomLeft: Radius.circular(12), bottomRight: reverse ? const Radius.circular(12) : Radius.zero,
bottomLeft: reverse ? Radius.zero : const Radius.circular(12),
), ),
), ),
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
@@ -230,22 +230,23 @@ class StreamChatThemeData {
), ),
), ),
channelPreviewTheme: ChannelPreviewTheme( channelPreviewTheme: ChannelPreviewTheme(
unreadCounterColor: colorTheme.accentRed, unreadCounterColor: colorTheme.accentRed,
avatarTheme: AvatarTheme( avatarTheme: AvatarTheme(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
constraints: const BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 40, height: 40,
width: 40, width: 40,
),
), ),
title: textTheme.bodyBold, ),
subtitle: textTheme.footnote.copyWith( title: textTheme.bodyBold,
color: const Color(0xff7A7A7A), subtitle: textTheme.footnote.copyWith(
), color: const Color(0xff7A7A7A),
lastMessageAt: textTheme.footnote.copyWith( ),
color: colorTheme.black.withOpacity(.5), lastMessageAt: textTheme.footnote.copyWith(
), color: colorTheme.black.withOpacity(.5),
indicatorIconSize: 16), ),
indicatorIconSize: 16,
),
channelListHeaderTheme: ChannelListHeaderTheme( channelListHeaderTheme: ChannelListHeaderTheme(
avatarTheme: AvatarTheme( avatarTheme: AvatarTheme(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
@@ -1186,7 +1187,8 @@ class MessageInputTheme {
actionButtonIdleColor: other.actionButtonIdleColor, actionButtonIdleColor: other.actionButtonIdleColor,
sendButtonColor: other.sendButtonColor, sendButtonColor: other.sendButtonColor,
sendButtonIdleColor: other.sendButtonIdleColor, sendButtonIdleColor: other.sendButtonIdleColor,
inputTextStyle: other.inputTextStyle, inputTextStyle:
inputTextStyle?.merge(other.inputTextStyle) ?? other.inputTextStyle,
inputDecoration: inputDecoration?.merge(other.inputDecoration) ?? inputDecoration: inputDecoration?.merge(other.inputDecoration) ??
other.inputDecoration, other.inputDecoration,
activeBorderGradient: other.activeBorderGradient, activeBorderGradient: other.activeBorderGradient,
@@ -101,6 +101,7 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
final chatThemeData = StreamChatTheme.of(context); final chatThemeData = StreamChatTheme.of(context);
return AppBar( return AppBar(
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
textTheme: Theme.of(context).textTheme,
brightness: Theme.of(context).brightness, brightness: Theme.of(context).brightness,
elevation: 1, elevation: 1,
leading: leading ?? leading: leading ??
@@ -28,9 +28,9 @@ class IVideoService {
/// ); /// );
/// debugPrint(info.toJson()); /// debugPrint(info.toJson());
/// ``` /// ```
Future<MediaInfo?> compressVideo(String? path) async => _lock.synchronized( Future<MediaInfo?> compressVideo(String path) async => _lock.synchronized(
() => VideoCompress.compressVideo( () => VideoCompress.compressVideo(
path!, path,
), ),
); );
+4 -4
View File
@@ -1,7 +1,7 @@
name: stream_chat_flutter name: stream_chat_flutter
homepage: https://github.com/GetStream/stream-chat-flutter homepage: https://github.com/GetStream/stream-chat-flutter
description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter. description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter.
version: 2.0.0-nullsafety.3 version: 2.0.0-nullsafety.4
repository: https://github.com/GetStream/stream-chat-flutter repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
@@ -30,13 +30,13 @@ dependencies:
lottie: ^1.0.1 lottie: ^1.0.1
meta: ^1.3.0 meta: ^1.3.0
path_provider: ^2.0.1 path_provider: ^2.0.1
photo_manager: ^1.1.4 photo_manager: ^1.1.6
photo_view: ^0.11.1 photo_view: ^0.11.1
rxdart: ^0.26.0 rxdart: ^0.27.0
scrollable_positioned_list: ^0.2.0-nullsafety.0 scrollable_positioned_list: ^0.2.0-nullsafety.0
share_plus: ^2.0.3 share_plus: ^2.0.3
shimmer: ^2.0.0 shimmer: ^2.0.0
stream_chat_flutter_core: ^2.0.0-nullsafety.2 stream_chat_flutter_core: ^2.0.0-nullsafety.3
substring_highlight: ^1.0.26 substring_highlight: ^1.0.26
synchronized: ^3.0.0 synchronized: ^3.0.0
url_launcher: ^6.0.3 url_launcher: ^6.0.3
@@ -1,3 +1,8 @@
## 2.0.0-nullsafety.3
* Update llc dependency
* Minor fixes and improvements
## 2.0.0-nullsafety.2 ## 2.0.0-nullsafety.2
* Fix ChannelsBloc not performing calls if pagination ended * Fix ChannelsBloc not performing calls if pagination ended
+1 -1
View File
@@ -1,4 +1,4 @@
# Official Core [Flutter SDK](https://getstream.io/chat/sdk/flutter/) for [Stream Chat API](https://getstream.io/chat/) # Official Core [Flutter SDK](https://getstream.io/chat/sdk/flutter/) for [Stream Chat](https://getstream.io/chat/)
> The official Flutter core components for Stream Chat, a service for > The official Flutter core components for Stream Chat, a service for
> building chat applications. > building chat applications.
@@ -0,0 +1,150 @@
analyzer:
enable-experiment:
- extension-methods
exclude:
- lib/**/*.g.dart
- example/**
- lib/src/emoji
- lib/**/*.freezed.dart
- test/**
linter:
rules:
# these rules are documented on and in the same order as
# the Dart Lint rules page to make maintenance easier
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
- always_use_package_imports
- avoid_empty_else
- avoid_relative_lib_imports
- avoid_slow_async_io
- avoid_types_as_parameter_names
- cancel_subscriptions
- close_sinks
- control_flow_in_finally
- empty_statements
- hash_and_equals
- invariant_booleans
- iterable_contains_unrelated_type
- list_remove_unrelated_type
- literal_only_boolean_expressions
- no_adjacent_strings_in_list
- no_duplicate_case_values
- no_logic_in_create_state
- prefer_void_to_null
- test_types_in_equals
- throw_in_finally
- unnecessary_statements
- unrelated_type_equality_checks
- omit_local_variable_types
- use_key_in_widget_constructors
- valid_regexps
- always_declare_return_types
- always_require_non_null_named_parameters
- annotate_overrides
- avoid_bool_literals_in_conditional_expressions
- avoid_catching_errors
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
- avoid_positional_boolean_parameters
- avoid_private_typedef_functions
- avoid_redundant_argument_values
- avoid_return_types_on_setters
- avoid_returning_null_for_void
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_unnecessary_containers
- avoid_unused_constructor_parameters
- await_only_futures
- camel_case_extensions
- camel_case_types
- cascade_invocations
- constant_identifier_names
- curly_braces_in_flow_control_structures
- directives_ordering
- empty_catches
- empty_constructor_bodies
- exhaustive_cases
- file_names
- implementation_imports
- join_return_with_assignment
- leading_newlines_in_multiline_strings
- library_names
- library_prefixes
- lines_longer_than_80_chars
- missing_whitespace_between_adjacent_strings
- non_constant_identifier_names
- null_closures
- one_member_abstracts
- only_throw_errors
- package_api_docs
- package_prefixed_library_names
- parameter_assignments
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_asserts_with_message
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_constructors_over_static_methods
- prefer_contains
- prefer_equal_for_default_values
- prefer_expression_function_bodies
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
- prefer_int_literals
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_null_aware_operators
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- provide_deprecation_message
- public_member_api_docs
- recursive_getters
- sized_box_for_whitespace
- slash_for_doc_comments
- sort_child_properties_last
- sort_constructors_first
- sort_unnamed_constructors_first
- type_annotate_public_apis
- type_init_formals
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
- unnecessary_lambdas
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_parenthesis
- unnecessary_raw_strings
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- use_is_even_rather_than_modulo
- use_late_for_private_fields_and_variables
- use_rethrow_when_possible
- use_setters_to_change_properties
- use_to_and_as_if_applicable
- package_names
- sort_pub_dependencies
- cast_nullable_to_non_nullable
- unnecessary_null_checks
- tighten_type_of_initializing_formals
- null_check_on_nullable_type_parameter
@@ -71,7 +71,7 @@ class ChannelsBlocState extends State<ChannelsBloc>
} }
/// The current channel list /// The current channel list
List<Channel>? get channels => _channelsController.value; List<Channel>? get channels => _channelsController.valueOrNull;
/// The current channel list as a stream /// The current channel list as a stream
Stream<List<Channel>> get channelsStream => _channelsController.stream; Stream<List<Channel>> get channelsStream => _channelsController.stream;
@@ -138,7 +138,7 @@ class ChannelsBlocState extends State<ChannelsBloc>
_channelsController.add(temp); _channelsController.add(temp);
} }
if (_channelsController.hasValue && if (_channelsController.hasValue &&
_queryChannelsLoadingController.value!) { _queryChannelsLoadingController.value) {
_queryChannelsLoadingController.sink.add(false); _queryChannelsLoadingController.sink.add(false);
} }
} }
@@ -44,7 +44,8 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
late StreamChatCoreState _streamChatCoreState; late StreamChatCoreState _streamChatCoreState;
/// The current messages list /// The current messages list
List<GetMessageResponse>? get messageResponses => _messageResponses.value; List<GetMessageResponse>? get messageResponses =>
_messageResponses.valueOrNull;
/// The current messages list as a stream /// The current messages list as a stream
Stream<List<GetMessageResponse>> get messagesStream => Stream<List<GetMessageResponse>> get messagesStream =>
@@ -93,8 +94,7 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
final temp = oldMessages + messages.results; final temp = oldMessages + messages.results;
_messageResponses.add(temp); _messageResponses.add(temp);
} }
if (_messageResponses.hasValue && if (_messageResponses.hasValue && _queryMessagesLoadingController.value) {
_queryMessagesLoadingController.value!) {
_queryMessagesLoadingController.add(false); _queryMessagesLoadingController.add(false);
} }
} catch (e, stk) { } catch (e, stk) {
@@ -43,7 +43,7 @@ class UsersBloc extends StatefulWidget {
class UsersBlocState extends State<UsersBloc> class UsersBlocState extends State<UsersBloc>
with AutomaticKeepAliveClientMixin { with AutomaticKeepAliveClientMixin {
/// The current users list /// The current users list
List<User>? get users => _usersController.value; List<User>? get users => _usersController.valueOrNull;
/// The current users list as a stream /// The current users list as a stream
Stream<List<User>> get usersStream => _usersController.stream; Stream<List<User>> get usersStream => _usersController.stream;
@@ -92,7 +92,7 @@ class UsersBlocState extends State<UsersBloc>
final temp = oldUsers + usersResponse.users; final temp = oldUsers + usersResponse.users;
_usersController.add(temp); _usersController.add(temp);
} }
if (_usersController.hasValue && _queryUsersLoadingController.value!) { if (_usersController.hasValue && _queryUsersLoadingController.value) {
_queryUsersLoadingController.add(false); _queryUsersLoadingController.add(false);
} }
} catch (e, stk) { } catch (e, stk) {
@@ -1,7 +1,7 @@
name: stream_chat_flutter_core name: stream_chat_flutter_core
homepage: https://github.com/GetStream/stream-chat-flutter homepage: https://github.com/GetStream/stream-chat-flutter
description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter. description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter.
version: 2.0.0-nullsafety.2 version: 2.0.0-nullsafety.3
repository: https://github.com/GetStream/stream-chat-flutter repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
@@ -10,12 +10,12 @@ environment:
flutter: ">=1.17.0" flutter: ">=1.17.0"
dependencies: dependencies:
collection: ^1.15.0-nullsafety.4 collection: ^1.15.0
flutter: flutter:
sdk: flutter sdk: flutter
meta: ^1.3.0 meta: ^1.3.0
rxdart: ^0.26.0 rxdart: ^0.27.0
stream_chat: ^2.0.0-nullsafety.1 stream_chat: ^2.0.0-nullsafety.2
dependency_overrides: dependency_overrides:
stream_chat: stream_chat:
@@ -1,3 +1,9 @@
## 2.0.0-nullsafety.2
* Update llc dependency
* Minor fixes and improvements
* Fixed bug not saving message.mentioned_users
## 2.0.0-nullsafety.1 ## 2.0.0-nullsafety.1
* Migrate this package to null safety * Migrate this package to null safety
@@ -0,0 +1,150 @@
analyzer:
enable-experiment:
- extension-methods
exclude:
- lib/**/*.g.dart
- example/**
- lib/src/emoji
- lib/**/*.freezed.dart
- test/**
linter:
rules:
# these rules are documented on and in the same order as
# the Dart Lint rules page to make maintenance easier
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
- always_use_package_imports
- avoid_empty_else
- avoid_relative_lib_imports
- avoid_slow_async_io
- avoid_types_as_parameter_names
- cancel_subscriptions
- close_sinks
- control_flow_in_finally
- empty_statements
- hash_and_equals
- invariant_booleans
- iterable_contains_unrelated_type
- list_remove_unrelated_type
- literal_only_boolean_expressions
- no_adjacent_strings_in_list
- no_duplicate_case_values
- no_logic_in_create_state
- prefer_void_to_null
- test_types_in_equals
- throw_in_finally
- unnecessary_statements
- unrelated_type_equality_checks
- omit_local_variable_types
- use_key_in_widget_constructors
- valid_regexps
- always_declare_return_types
- always_require_non_null_named_parameters
- annotate_overrides
- avoid_bool_literals_in_conditional_expressions
- avoid_catching_errors
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
- avoid_positional_boolean_parameters
- avoid_private_typedef_functions
- avoid_redundant_argument_values
- avoid_return_types_on_setters
- avoid_returning_null_for_void
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_unnecessary_containers
- avoid_unused_constructor_parameters
- await_only_futures
- camel_case_extensions
- camel_case_types
- cascade_invocations
- constant_identifier_names
- curly_braces_in_flow_control_structures
- directives_ordering
- empty_catches
- empty_constructor_bodies
- exhaustive_cases
- file_names
- implementation_imports
- join_return_with_assignment
- leading_newlines_in_multiline_strings
- library_names
- library_prefixes
- lines_longer_than_80_chars
- missing_whitespace_between_adjacent_strings
- non_constant_identifier_names
- null_closures
- one_member_abstracts
- only_throw_errors
- package_api_docs
- package_prefixed_library_names
- parameter_assignments
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_asserts_with_message
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_constructors_over_static_methods
- prefer_contains
- prefer_equal_for_default_values
- prefer_expression_function_bodies
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
- prefer_int_literals
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_null_aware_operators
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- provide_deprecation_message
- public_member_api_docs
- recursive_getters
- sized_box_for_whitespace
- slash_for_doc_comments
- sort_child_properties_last
- sort_constructors_first
- sort_unnamed_constructors_first
- type_annotate_public_apis
- type_init_formals
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
- unnecessary_lambdas
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_parenthesis
- unnecessary_raw_strings
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- use_is_even_rather_than_modulo
- use_late_for_private_fields_and_variables
- use_rethrow_when_possible
- use_setters_to_change_properties
- use_to_and_as_if_applicable
- package_names
- sort_pub_dependencies
- cast_nullable_to_non_nullable
- unnecessary_null_checks
- tighten_type_of_initializing_formals
- null_check_on_nullable_type_parameter
@@ -1,5 +1,3 @@
import 'package:meta/meta.dart';
import 'package:moor/ffi.dart';
import 'package:moor/moor.dart'; import 'package:moor/moor.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
@@ -46,10 +44,6 @@ class MoorChatDatabase extends _$MoorChatDatabase {
DatabaseConnection connection, DatabaseConnection connection,
) : super.connect(connection); ) : super.connect(connection);
/// Custom constructor used only for testing
@visibleForTesting
MoorChatDatabase.testable(this._userId) : super(VmDatabase.memory());
final String _userId; final String _userId;
/// User id to which the database is connected /// User id to which the database is connected
@@ -1,7 +1,7 @@
name: stream_chat_persistence name: stream_chat_persistence
homepage: https://github.com/GetStream/stream-chat-flutter homepage: https://github.com/GetStream/stream-chat-flutter
description: Official Stream Chat Persistence library. Build your own chat experience using Dart and Flutter. description: Official Stream Chat Persistence library. Build your own chat experience using Dart and Flutter.
version: 2.0.0-nullsafety.1 version: 2.0.0-nullsafety.2
repository: https://github.com/GetStream/stream-chat-flutter repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
@@ -17,8 +17,8 @@ dependencies:
mutex: ^3.0.0 mutex: ^3.0.0
path: ^1.8.0 path: ^1.8.0
path_provider: ^2.0.1 path_provider: ^2.0.1
sqlite3_flutter_libs: ^0.4.1 sqlite3_flutter_libs: ^0.4.2
stream_chat: ^2.0.0-nullsafety.0 stream_chat: ^2.0.0-nullsafety.2
dependency_overrides: dependency_overrides:
stream_chat: stream_chat:
@@ -3,12 +3,14 @@ import 'package:stream_chat_persistence/src/dao/channel_dao.dart';
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../../stream_chat_persistence_client_test.dart';
void main() { void main() {
late ChannelDao channelDao; late ChannelDao channelDao;
late MoorChatDatabase database; late MoorChatDatabase database;
setUp(() { setUp(() {
database = MoorChatDatabase.testable('testUserId'); database = testDatabaseProvider('testUserId');
channelDao = database.channelDao; channelDao = database.channelDao;
}); });
@@ -5,6 +5,7 @@ import 'package:stream_chat_persistence/src/dao/channel_query_dao.dart';
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../../stream_chat_persistence_client_test.dart';
import '../utils/date_matcher.dart'; import '../utils/date_matcher.dart';
void main() { void main() {
@@ -12,7 +13,7 @@ void main() {
late ChannelQueryDao channelQueryDao; late ChannelQueryDao channelQueryDao;
setUp(() { setUp(() {
database = MoorChatDatabase.testable('testUserId'); database = testDatabaseProvider('testUserId');
channelQueryDao = database.channelQueryDao; channelQueryDao = database.channelQueryDao;
}); });
@@ -3,6 +3,7 @@ import 'package:stream_chat_persistence/src/dao/connection_event_dao.dart';
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../../stream_chat_persistence_client_test.dart';
import '../utils/date_matcher.dart'; import '../utils/date_matcher.dart';
void main() { void main() {
@@ -10,7 +11,7 @@ void main() {
late MoorChatDatabase database; late MoorChatDatabase database;
setUp(() { setUp(() {
database = MoorChatDatabase.testable('testUserId'); database = testDatabaseProvider('testUserId');
eventDao = database.connectionEventDao; eventDao = database.connectionEventDao;
}); });
@@ -5,6 +5,7 @@ import 'package:stream_chat_persistence/src/dao/dao.dart';
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../../stream_chat_persistence_client_test.dart';
import '../utils/date_matcher.dart'; import '../utils/date_matcher.dart';
void main() { void main() {
@@ -12,7 +13,7 @@ void main() {
late MoorChatDatabase database; late MoorChatDatabase database;
setUp(() { setUp(() {
database = MoorChatDatabase.testable('testUserId'); database = testDatabaseProvider('testUserId');
memberDao = database.memberDao; memberDao = database.memberDao;
}); });
@@ -5,12 +5,14 @@ import 'package:stream_chat_persistence/src/dao/dao.dart';
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../../stream_chat_persistence_client_test.dart';
void main() { void main() {
late MessageDao messageDao; late MessageDao messageDao;
late MoorChatDatabase database; late MoorChatDatabase database;
setUp(() { setUp(() {
database = MoorChatDatabase.testable('testUserId'); database = testDatabaseProvider('testUserId');
messageDao = database.messageDao; messageDao = database.messageDao;
}); });
@@ -5,12 +5,14 @@ import 'package:stream_chat_persistence/src/dao/dao.dart';
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../../stream_chat_persistence_client_test.dart';
void main() { void main() {
late PinnedMessageDao pinnedMessageDao; late PinnedMessageDao pinnedMessageDao;
late MoorChatDatabase database; late MoorChatDatabase database;
setUp(() { setUp(() {
database = MoorChatDatabase.testable('testUserId'); database = testDatabaseProvider('testUserId');
pinnedMessageDao = database.pinnedMessageDao; pinnedMessageDao = database.pinnedMessageDao;
}); });
@@ -5,12 +5,14 @@ import 'package:stream_chat_persistence/src/dao/reaction_dao.dart';
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../../stream_chat_persistence_client_test.dart';
void main() { void main() {
late ReactionDao reactionDao; late ReactionDao reactionDao;
late MoorChatDatabase database; late MoorChatDatabase database;
setUp(() { setUp(() {
database = MoorChatDatabase.testable('testUserId'); database = testDatabaseProvider('testUserId');
reactionDao = database.reactionDao; reactionDao = database.reactionDao;
}); });
@@ -3,6 +3,7 @@ import 'package:stream_chat_persistence/src/dao/dao.dart';
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../../stream_chat_persistence_client_test.dart';
import '../utils/date_matcher.dart'; import '../utils/date_matcher.dart';
void main() { void main() {
@@ -10,7 +11,7 @@ void main() {
late MoorChatDatabase database; late MoorChatDatabase database;
setUp(() { setUp(() {
database = MoorChatDatabase.testable('testUserId'); database = testDatabaseProvider('testUserId');
readDao = database.readDao; readDao = database.readDao;
}); });
@@ -5,12 +5,14 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import '../../stream_chat_persistence_client_test.dart';
void main() { void main() {
late UserDao userDao; late UserDao userDao;
late MoorChatDatabase database; late MoorChatDatabase database;
setUp(() { setUp(() {
database = MoorChatDatabase.testable('testUserId'); database = testDatabaseProvider('testUserId');
userDao = database.userDao; userDao = database.userDao;
}); });
@@ -1,4 +1,5 @@
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:moor/ffi.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart'; import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart';
@@ -6,8 +7,8 @@ import 'package:test/test.dart';
import 'mock_chat_database.dart'; import 'mock_chat_database.dart';
import 'src/utils/date_matcher.dart'; import 'src/utils/date_matcher.dart';
MoorChatDatabase _testDatabaseProvider(String userId, ConnectionMode mode) => MoorChatDatabase testDatabaseProvider(String userId, [ConnectionMode? mode]) =>
MoorChatDatabase.testable(userId); MoorChatDatabase(userId, VmDatabase.memory());
void main() { void main() {
group('connect', () { group('connect', () {
@@ -15,7 +16,7 @@ void main() {
test('successfully connects with the Database', () async { test('successfully connects with the Database', () async {
final client = StreamChatPersistenceClient(logLevel: Level.ALL); final client = StreamChatPersistenceClient(logLevel: Level.ALL);
expect(client.db, isNull); expect(client.db, isNull);
await client.connect(userId, databaseProvider: _testDatabaseProvider); await client.connect(userId, databaseProvider: testDatabaseProvider);
expect(client.db, isNotNull); expect(client.db, isNotNull);
expect(client.db, isA<MoorChatDatabase>()); expect(client.db, isA<MoorChatDatabase>());
expect(client.db!.userId, userId); expect(client.db!.userId, userId);
@@ -28,13 +29,13 @@ void main() {
test('throws if already connected', () async { test('throws if already connected', () async {
final client = StreamChatPersistenceClient(logLevel: Level.ALL); final client = StreamChatPersistenceClient(logLevel: Level.ALL);
expect(client.db, isNull); expect(client.db, isNull);
await client.connect(userId, databaseProvider: _testDatabaseProvider); await client.connect(userId, databaseProvider: testDatabaseProvider);
expect(client.db, isNotNull); expect(client.db, isNotNull);
expect(client.db, isNotNull); expect(client.db, isNotNull);
expect(client.db, isA<MoorChatDatabase>()); expect(client.db, isA<MoorChatDatabase>());
expect(client.db!.userId, userId); expect(client.db!.userId, userId);
expect( expect(
() => client.connect(userId, databaseProvider: _testDatabaseProvider), () => client.connect(userId, databaseProvider: testDatabaseProvider),
throwsException, throwsException,
); );
@@ -47,7 +48,7 @@ void main() {
test('disconnect', () async { test('disconnect', () async {
const userId = 'testUserId'; const userId = 'testUserId';
final client = StreamChatPersistenceClient(logLevel: Level.ALL); final client = StreamChatPersistenceClient(logLevel: Level.ALL);
await client.connect(userId, databaseProvider: _testDatabaseProvider); await client.connect(userId, databaseProvider: testDatabaseProvider);
expect(client.db, isNotNull); expect(client.db, isNotNull);
await client.disconnect(flush: true); await client.disconnect(flush: true);
expect(client.db, isNull); expect(client.db, isNull);