diff --git a/.github/workflows/legacy_version_analyze.yml b/.github/workflows/legacy_version_analyze.yml index 89c195dfe..4b462c524 100644 --- a/.github/workflows/legacy_version_analyze.yml +++ b/.github/workflows/legacy_version_analyze.yml @@ -3,7 +3,7 @@ name: legacy_version_analyze env: # Note: The versions below should be manually updated after a new stable # version comes out. - flutter_version: "3.7.12" + flutter_version: "3.10.6" on: push: diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml index 99312316c..5f7c04889 100644 --- a/.github/workflows/stream_flutter_workflow.yml +++ b/.github/workflows/stream_flutter_workflow.yml @@ -2,7 +2,7 @@ name: stream_flutter_workflow env: ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' - flutter_version: "3.10.4" + flutter_channel: "stable" on: pull_request: @@ -37,7 +37,7 @@ jobs: uses: subosito/flutter-action@v2 with: cache: true - flutter-version: ${{ env.flutter_version }} + channel: ${{ env.flutter_channel }} - name: "Install Tools" run: | flutter pub global activate melos diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index f7f43289c..762f4ad36 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,3 +1,18 @@ +## 6.8.0 + +🐞 Fixed + +- Fixed `Channel.query` not initializing `ChannelState`. + +✅ Added + +- Added support for `channel.countUnreadMentions()` to get the count of unread messages mentioning the current user on a + channel. [#1692](https://github.com/GetStream/stream-chat-flutter/issues/1692) + +🔄 Changed + +- Updated minimum supported `SDK` version to Dart 3.0 + ## 6.7.0 ✅ Added diff --git a/packages/stream_chat/example/pubspec.yaml b/packages/stream_chat/example/pubspec.yaml index 16362f020..d16c8647d 100644 --- a/packages/stream_chat/example/pubspec.yaml +++ b/packages/stream_chat/example/pubspec.yaml @@ -5,8 +5,8 @@ publish_to: "none" version: 1.0.0+1 environment: - sdk: '>=2.19.0 <4.0.0' - flutter: ">=3.7.0" + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" dependencies: cupertino_icons: ^1.0.5 diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index 838f267c1..f588205d7 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -1339,26 +1339,6 @@ class Channel { return _client.markChannelRead(id!, type, messageId: messageId); } - /// Loads the initial channel state and watches for changes. - Future watch({bool presence = false}) async { - ChannelState response; - - try { - response = await query(watch: true, presence: presence); - } catch (error, stackTrace) { - if (!_initializedCompleter.isCompleted) { - _initializedCompleter.completeError(error, stackTrace); - } - rethrow; - } - - if (state == null) { - _initState(response); - } - - return response; - } - void _initState(ChannelState channelState) { state = ChannelClientState(this, channelState); @@ -1370,6 +1350,22 @@ class Channel { } } + /// Loads the initial channel state and watches for changes. + Future watch({ + bool presence = false, + PaginationParams? messagesPagination, + PaginationParams? membersPagination, + PaginationParams? watchersPagination, + }) { + return query( + watch: true, + presence: presence, + messagesPagination: messagesPagination, + membersPagination: membersPagination, + watchersPagination: watchersPagination, + ); + } + /// Stop watching the channel. Future stopWatching() async { _checkInitialized(); @@ -1435,7 +1431,7 @@ class Channel { ); /// Creates a new channel. - Future create() async => query(state: false); + Future create() => query(state: false); /// Query the API, get messages, members or other channel fields. /// @@ -1450,23 +1446,28 @@ class Channel { PaginationParams? watchersPagination, bool preferOffline = false, }) async { - if (preferOffline && cid != null) { - final updatedState = await _client.chatPersistenceClient - ?.getChannelStateByCid(cid!, messagePagination: messagesPagination); - if (updatedState != null && - updatedState.messages != null && - updatedState.messages!.isNotEmpty) { - if (this.state == null) { - _initState(updatedState); - } else { - this.state?.updateChannelState(updatedState); + ChannelState? channelState; + + try { + // If we prefer offline, we first try to get the channel state from the + // offline storage. + if (preferOffline && !watch && cid != null) { + final persistenceClient = _client.chatPersistenceClient; + if (persistenceClient != null) { + final cachedState = await persistenceClient.getChannelStateByCid( + cid!, + messagePagination: messagesPagination, + ); + + // If the cached state contains messages, we can use it. + if (cachedState.messages?.isNotEmpty == true) { + channelState = cachedState; + } } - return updatedState; } - } - try { - final updatedState = await _client.queryChannel( + // If we still don't have the channelState, we try to get it from the API. + channelState ??= await _client.queryChannel( type, channelId: id, channelData: _extraData, @@ -1479,18 +1480,35 @@ class Channel { ); if (_id == null) { - _id = updatedState.channel!.id; - _cid = updatedState.channel!.cid; + _id = channelState.channel!.id; + _cid = channelState.channel!.cid; } - this.state?.updateChannelState(updatedState); - return updatedState; - } catch (e) { - if (_client.persistenceEnabled) { - return _client.chatPersistenceClient!.getChannelStateByCid( - cid!, - messagePagination: messagesPagination, - ); + // Initialize the channel state if it's not initialized yet. + if (this.state == null) { + _initState(channelState); + } else { + // Otherwise, update the channel state. + this.state?.updateChannelState(channelState); + } + + return channelState; + } catch (e, stk) { + // If we failed to get the channel state from the API and we were not + // supposed to watch the channel, we will try to get the channel state + // from the offline storage. + if (watch == false) { + if (_client.persistenceEnabled) { + return _client.chatPersistenceClient!.getChannelStateByCid( + cid!, + messagePagination: messagesPagination, + ); + } + } + + // Otherwise, we will just rethrow the error. + if (!_initializedCompleter.isCompleted) { + _initializedCompleter.completeError(e, stk); } rethrow; @@ -2334,6 +2352,26 @@ class ChannelClientState { !isThreadMessage; } + /// Counts the number of unread messages mentioning the current user. + /// + /// **NOTE**: The method relies on the [Channel.messages] list and doesn't do + /// any API call. Therefore, the count might be not reliable as it relies on + /// the local data. + int countUnreadMentions() { + final lastRead = currentUserRead?.lastRead; + final userId = _channel.client.state.currentUser?.id; + + var count = 0; + for (final message in messages) { + if (_countMessageAsUnread(message) && + (lastRead == null || message.createdAt.isAfter(lastRead)) && + message.mentionedUsers.any((user) => user.id == userId) == true) { + count++; + } + } + return count; + } + /// Update threads with updated information about messages. void updateThreadInfo(String parentId, List messages) { final newThreads = Map>.from(threads); diff --git a/packages/stream_chat/lib/src/client/client.dart b/packages/stream_chat/lib/src/client/client.dart index 9fc995312..4e4fc5e45 100644 --- a/packages/stream_chat/lib/src/client/client.dart +++ b/packages/stream_chat/lib/src/client/client.dart @@ -1696,7 +1696,7 @@ class ClientState { void updateUsers(List userList) { final newUsers = { ...users, - for (var user in userList) + for (final user in userList) if (user != null) user.id: user, }; _usersController.add(newUsers); diff --git a/packages/stream_chat/lib/src/ws/timer_helper.dart b/packages/stream_chat/lib/src/ws/timer_helper.dart index 0974ee80c..1478327cc 100644 --- a/packages/stream_chat/lib/src/ws/timer_helper.dart +++ b/packages/stream_chat/lib/src/ws/timer_helper.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:uuid/uuid.dart'; /// -class TimerHelper { +mixin class TimerHelper { final _uuid = const Uuid(); late final _timers = {}; diff --git a/packages/stream_chat/lib/version.dart b/packages/stream_chat/lib/version.dart index 208206227..085db925c 100644 --- a/packages/stream_chat/lib/version.dart +++ b/packages/stream_chat/lib/version.dart @@ -3,4 +3,4 @@ import 'package:stream_chat/src/client/client.dart'; /// Current package version /// Used in [StreamChatClient] to build the `x-stream-client` header // ignore: constant_identifier_names -const PACKAGE_VERSION = '6.7.0'; +const PACKAGE_VERSION = '6.8.0'; diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index 5e8144e4e..b03685f4c 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -1,24 +1,24 @@ name: stream_chat homepage: https://getstream.io/ description: The official Dart client for Stream Chat, a service for building chat applications. -version: 6.7.0 +version: 6.8.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues environment: - sdk: '>=2.19.0 <4.0.0' + sdk: '>=3.0.0 <4.0.0' dependencies: - async: ^2.10.0 - collection: ^1.17.0 - dio: ^5.2.1+1 + async: ^2.11.0 + collection: ^1.17.1 + dio: ^5.3.2 equatable: ^2.0.5 - freezed_annotation: ^2.2.0 + freezed_annotation: ^2.4.1 http_parser: ^4.0.2 - jose: ^0.3.3 + jose: ^0.3.4 json_annotation: ^4.8.1 logging: ^1.2.0 - meta: ^1.8.0 + meta: ^1.9.1 mime: ^1.0.4 rate_limiter: ^1.0.0 rxdart: ^0.27.7 @@ -27,8 +27,8 @@ dependencies: web_socket_channel: ^2.4.0 dev_dependencies: - build_runner: ^2.3.3 - freezed: ^2.4.0 - json_serializable: ^6.6.2 - mocktail: ^0.3.0 - test: ^1.24.3 + build_runner: ^2.4.6 + freezed: ^2.4.2 + json_serializable: ^6.7.1 + mocktail: ^1.0.0 + test: ^1.24.6 diff --git a/packages/stream_chat/test/src/client/client_test.dart b/packages/stream_chat/test/src/client/client_test.dart index df167b235..87053d9bf 100644 --- a/packages/stream_chat/test/src/client/client_test.dart +++ b/packages/stream_chat/test/src/client/client_test.dart @@ -975,7 +975,7 @@ void main() { // skipping initial seed event -> {} users client.state.usersStream.skip(1), emitsInOrder([ - {for (var user in users) user.id: user}, + {for (final user in users) user.id: user}, ]), ); diff --git a/packages/stream_chat/test/src/core/http/interceptor/additional_headers_interceptor_test.dart b/packages/stream_chat/test/src/core/http/interceptor/additional_headers_interceptor_test.dart index 88cf42ae5..5a30bd5be 100644 --- a/packages/stream_chat/test/src/core/http/interceptor/additional_headers_interceptor_test.dart +++ b/packages/stream_chat/test/src/core/http/interceptor/additional_headers_interceptor_test.dart @@ -1,3 +1,5 @@ +// ignore_for_file: invalid_use_of_protected_member + import 'package:dio/dio.dart'; import 'package:stream_chat/src/core/http/interceptor/additional_headers_interceptor.dart'; import 'package:stream_chat/stream_chat.dart'; diff --git a/packages/stream_chat/test/src/core/http/interceptor/auth_interceptor_test.dart b/packages/stream_chat/test/src/core/http/interceptor/auth_interceptor_test.dart index e5d066007..be982d479 100644 --- a/packages/stream_chat/test/src/core/http/interceptor/auth_interceptor_test.dart +++ b/packages/stream_chat/test/src/core/http/interceptor/auth_interceptor_test.dart @@ -1,3 +1,5 @@ +// ignore_for_file: invalid_use_of_protected_member + import 'package:dio/dio.dart'; import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/src/core/http/interceptor/auth_interceptor.dart'; diff --git a/packages/stream_chat/test/src/core/http/interceptor/connection_id_interceptor_test.dart b/packages/stream_chat/test/src/core/http/interceptor/connection_id_interceptor_test.dart index 9acabccf3..92d391851 100644 --- a/packages/stream_chat/test/src/core/http/interceptor/connection_id_interceptor_test.dart +++ b/packages/stream_chat/test/src/core/http/interceptor/connection_id_interceptor_test.dart @@ -1,3 +1,5 @@ +// ignore_for_file: invalid_use_of_protected_member + import 'package:dio/dio.dart'; import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/src/core/http/connection_id_manager.dart'; diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 7d2bac4fc..5d3e46b09 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,3 +1,38 @@ +## 6.9.0 + +🐞 Fixed + +- [[#1702]](https://github.com/GetStream/stream-chat-flutter/issues/1702) + Fixed `Message.replaceMentions` not treating `@usernames` as mentions. +- [[#1694]](https://github.com/GetStream/stream-chat-flutter/issues/1694) Fixed Video player buttons + getting covered by bottom toolbar. + +✅ Added + +- Added support for listening error events in AttachmentPickerBottomSheet. +- Added support for overriding the `MessageWidget.onReactionTap` callback. +- Added support for `StreamMessageInput.contentInsertionConfiguration` to specify the content insertion configuration. + [#1613](https://github.com/GetStream/stream-chat-flutter/issues/1613) + + ```dart + StreamMessageInput( + ..., + contentInsertionConfiguration: ContentInsertionConfiguration( + onContentInserted: (content) { + // Do something with the content. + controller.addAttachment(...); + }, + ), + ) + ``` + +🔄 Changed + +- Updated minimum supported `SDK` version to Flutter 3.10/Dart 3.0 +- Updated `stream_chat_flutter_core` dependency + to [`6.8.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog). +- Updated jiffy dependency to ^6.2.1. + ## 6.8.1 🐞 Fixed @@ -28,43 +63,57 @@ 🐞 Fixed -- [[#1620]](https://github.com/GetStream/stream-chat-flutter/issues/1620) Fixed messages Are Not Hard Deleting even +- [[#1620]](https://github.com/GetStream/stream-chat-flutter/issues/1620) Fixed messages Are Not + Hard Deleting even after overriding the `onConfirmDeleteTap` callback. -- [[#1621]](https://github.com/GetStream/stream-chat-flutter/issues/1621) Fixed `createdAtStyle` null check error +- [[#1621]](https://github.com/GetStream/stream-chat-flutter/issues/1621) Fixed `createdAtStyle` + null check error in `SendingIndicatorBuilder`. -- [[#1069]](https://github.com/GetStream/stream-chat-flutter/issues/1069) Fixed message swipe to reply using same - direction for both current user and other users. It now uses `SwipeDirection.startToEnd` for current user +- [[#1069]](https://github.com/GetStream/stream-chat-flutter/issues/1069) Fixed message swipe to + reply using same + direction for both current user and other users. It now uses `SwipeDirection.startToEnd` for + current user and `SwipeDirection.endToStart` for other users. - [[#1590]](https://github.com/GetStream/stream-chat-flutter/issues/1590) - Fixed `StreamMessageWidget.showReactionPickerIndicator` not toggling the reaction picker indicator visibility. -- [[#1639]](https://github.com/GetStream/stream-chat-flutter/issues/1639) Fixed attachments not showing in gallery view + Fixed `StreamMessageWidget.showReactionPickerIndicator` not toggling the reaction picker indicator + visibility. +- [[#1639]](https://github.com/GetStream/stream-chat-flutter/issues/1639) Fixed attachments not + showing in gallery view even after saving them to the device. > **Note** - > This fix depends on the [image_gallery_saver](https://pub.dev/packages/image_gallery_saver) plugin. Make sure to add + > This fix depends on the [image_gallery_saver](https://pub.dev/packages/image_gallery_saver) + plugin. Make sure to add necessary permissions in your App as per the plugin documentation. -- [[#1642]](https://github.com/GetStream/stream-chat-flutter/issues/1642) Fixed `StreamMessageWidget.widthFactor` not +- [[#1642]](https://github.com/GetStream/stream-chat-flutter/issues/1642) + Fixed `StreamMessageWidget.widthFactor` not working on web and desktop platforms. ✅ Added -- Added support for customizing attachments in `StreamMessageInput`. Use various properties mentioned +- Added support for customizing attachments in `StreamMessageInput`. Use various properties + mentioned below. [#1511](https://github.com/GetStream/stream-chat-flutter/issues/1511) * `StreamMessageInput.attachmentListBuilder` to customize the attachment list. * `StreamMessageInput.fileAttachmentListBuilder` to customize the file attachment list. - * `StreamMessageInput.mediaAttachmentListBuilder` to customize the media attachment list. Includes images, videos + * `StreamMessageInput.mediaAttachmentListBuilder` to customize the media attachment list. + Includes images, videos and gifs. - * `StreamMessageInput.fileAttachmentBuilder` to customize the file attachment item shown in `FileAttachmentList`. + * `StreamMessageInput.fileAttachmentBuilder` to customize the file attachment item shown + in `FileAttachmentList`. * `StreamMessageInput.mediaAttachmentBuilder` to customize the media attachment item shown in `MediaAttachmentList`. -- Added `StreamMessageInput.quotedMessageAttachmentThumbnailBuilders` to customize the thumbnail builders for quoted +- Added `StreamMessageInput.quotedMessageAttachmentThumbnailBuilders` to customize the thumbnail + builders for quoted message attachments. 🔄 Changed -- Deprecated `StreamMessageInput.attachmentThumbnailBuilders` in favor of `StreamMessageInput.mediaAttachmentBuilder`. -- Deprecated `StreamMessageListView.onMessageSwiped`. Try wrapping the `MessageWidget` with a `Swipeable`, `Dismissible` +- Deprecated `StreamMessageInput.attachmentThumbnailBuilders` in favor + of `StreamMessageInput.mediaAttachmentBuilder`. +- Deprecated `StreamMessageListView.onMessageSwiped`. Try wrapping the `MessageWidget` with + a `Swipeable`, `Dismissible` or a custom widget to achieve the swipe to reply behaviour. ```dart @@ -112,7 +161,8 @@ }, ) ``` -- Deprecated `StreamMessageWidget.showReactionPickerIndicator` in favor of `StreamMessageWidget.showReactionPicker`. +- Deprecated `StreamMessageWidget.showReactionPickerIndicator` in favor + of `StreamMessageWidget.showReactionPicker`. ```diff StreamMessageWidget( @@ -129,16 +179,20 @@ 🐞 Fixed -- [[#1600]](https://github.com/GetStream/stream-chat-flutter/issues/1600) Fixed type `ImageDecoderCallback` not found +- [[#1600]](https://github.com/GetStream/stream-chat-flutter/issues/1600) Fixed + type `ImageDecoderCallback` not found error on pre-Flutter 3.10.0 versions. -- [[#1605]](https://github.com/GetStream/stream-chat-flutter/issues/1605) Fixed Null exception is thrown on message list +- [[#1605]](https://github.com/GetStream/stream-chat-flutter/issues/1605) Fixed Null exception is + thrown on message list for unread messages when `ScrollToBottomButton` is pressed. -- [[#1615]](https://github.com/GetStream/stream-chat-flutter/issues/1615) Fixed `StreamAttachmentPickerBottomSheet` not +- [[#1615]](https://github.com/GetStream/stream-chat-flutter/issues/1615) + Fixed `StreamAttachmentPickerBottomSheet` not able to find the `StreamChatTheme` when used in nested MaterialApp. ✅ Added -- Added support for `StreamMessageInput.allowedAttachmentPickerTypes` to specify the allowed attachment picker types. +- Added support for `StreamMessageInput.allowedAttachmentPickerTypes` to specify the allowed + attachment picker types. [#1601](https://github.com/GetStream/stream-chat-flutter/issues/1376) ```dart @@ -151,7 +205,8 @@ ) ``` -- Added support for `StreamMessageWidget.onConfirmDeleteTap` to override the default action on delete confirmation. +- Added support for `StreamMessageWidget.onConfirmDeleteTap` to override the default action on + delete confirmation. [#1604](https://github.com/GetStream/stream-chat-flutter/issues/1604) ```dart @@ -164,8 +219,10 @@ ) ``` -- Added support for `StreamMessageWidget.quotedMessageBuilder` and `StreamMessageInput.quotedMessageBuilder` to override - the default quoted message widget. [#1547](https://github.com/GetStream/stream-chat-flutter/issues/1547) +- Added support for `StreamMessageWidget.quotedMessageBuilder` + and `StreamMessageInput.quotedMessageBuilder` to override + the default quoted message + widget. [#1547](https://github.com/GetStream/stream-chat-flutter/issues/1547) ```dart StreamMessageWidget( @@ -179,7 +236,8 @@ ) ``` -- Added support for `StreamChannelAvatar.ownSpaceAvatarBuilder`, `StreamChannelAvatar.oneToOneAvatarBuilder` and +- Added support + for `StreamChannelAvatar.ownSpaceAvatarBuilder`, `StreamChannelAvatar.oneToOneAvatarBuilder` and `StreamChannelAvatar.groupAvatarBuilder` to override the default avatar widget.[#1614](https://github.com/GetStream/stream-chat-flutter/issues/1614) @@ -211,10 +269,13 @@ 🐞 Fixed -- [[#1592]](https://github.com/GetStream/stream-chat-flutter/issues/1592) Fixed broken attachment download on web. -- [[#1591]](https://github.com/GetStream/stream-chat-flutter/issues/1591) Fixed `StreamChannelInfoBottomSheet` not +- [[#1592]](https://github.com/GetStream/stream-chat-flutter/issues/1592) Fixed broken attachment + download on web. +- [[#1591]](https://github.com/GetStream/stream-chat-flutter/issues/1591) + Fixed `StreamChannelInfoBottomSheet` not rendering member list properly. -- [[#1427]](https://github.com/GetStream/stream-chat-flutter/issues/1427) Fixed unable to load asset error for +- [[#1427]](https://github.com/GetStream/stream-chat-flutter/issues/1427) Fixed unable to load asset + error for `packages/stream_chat_flutter/lib/svgs/video_call_icon.svg`. 🔄 Changed @@ -227,30 +288,40 @@ - [[#1546]](https://github.com/GetStream/stream-chat-flutter/issues/1546) Fixed `StreamMessageInputTheme.linkHighlightColor` returning null for default theme. -- [[#1548]](https://github.com/GetStream/stream-chat-flutter/issues/1548) Fixed `StreamMessageInput` urlRegex only +- [[#1548]](https://github.com/GetStream/stream-chat-flutter/issues/1548) Fixed `StreamMessageInput` + urlRegex only matching the lowercase `http(s)|ftp`. -- [[#1542]](https://github.com/GetStream/stream-chat-flutter/issues/1542) Handle error thrown in `StreamMessageInput` +- [[#1542]](https://github.com/GetStream/stream-chat-flutter/issues/1542) Handle error thrown + in `StreamMessageInput` when unable to fetch a link preview. -- [[#1540]](https://github.com/GetStream/stream-chat-flutter/issues/1540) Use `CircularProgressIndicator.adaptive` +- [[#1540]](https://github.com/GetStream/stream-chat-flutter/issues/1540) + Use `CircularProgressIndicator.adaptive` instead of material indicator. -- [[#1490]](https://github.com/GetStream/stream-chat-flutter/issues/1490) Fixed `editMessageInputBuilder` property not +- [[#1490]](https://github.com/GetStream/stream-chat-flutter/issues/1490) + Fixed `editMessageInputBuilder` property not used in `MessageActionsModal.editMessage` option. -- [[#1544]](https://github.com/GetStream/stream-chat-flutter/issues/1544) Fixed error thrown when unable to fetch +- [[#1544]](https://github.com/GetStream/stream-chat-flutter/issues/1544) Fixed error thrown when + unable to fetch image/data in Message link preview. -- [[#1482]](https://github.com/GetStream/stream-chat-flutter/issues/1482) Fixed `StreaChannelListTile` not showing +- [[#1482]](https://github.com/GetStream/stream-chat-flutter/issues/1482) + Fixed `StreaChannelListTile` not showing unread indicator when `currentUser` is not present in the initial member list. - [[#1487]](https://github.com/GetStream/stream-chat-flutter/issues/1487) Use localized title for `WebOrDesktopAttachmentPickerOption` in `StreamMessageInput`. -- [[#1250]](https://github.com/GetStream/stream-chat-flutter/issues/1250) Fixed bottomRow widgetSpans getting resized +- [[#1250]](https://github.com/GetStream/stream-chat-flutter/issues/1250) Fixed bottomRow + widgetSpans getting resized twice when `textScaling` is enabled. -- [[#1498]](https://github.com/GetStream/stream-chat-flutter/issues/1498) Fixed `MessageInput` autocomplete not working +- [[#1498]](https://github.com/GetStream/stream-chat-flutter/issues/1498) Fixed `MessageInput` + autocomplete not working on non-mobile platforms. -- [[#1576]](https://github.com/GetStream/stream-chat-flutter/issues/1576) Temporary fix for `StreamMessageListView` +- [[#1576]](https://github.com/GetStream/stream-chat-flutter/issues/1576) Temporary fix + for `StreamMessageListView` getting broken when loaded at a particular message and a new message is added. ✅ Added -- Added support for `StreamMessageThemeData.urlAttachmentTextMaxLine` to specify the `.maxLines` for the url attachment +- Added support for `StreamMessageThemeData.urlAttachmentTextMaxLine` to specify the `.maxLines` for + the url attachment text. [#1543](https://github.com/GetStream/stream-chat-flutter/issues/1543) 🔄 Changed @@ -265,24 +336,33 @@ 🐞 Fixed -- [[#1502]](https://github.com/GetStream/stream-chat-flutter/issues/1502) Fixed `isOnlyEmoji` method Detects Single +- [[#1502]](https://github.com/GetStream/stream-chat-flutter/issues/1502) Fixed `isOnlyEmoji` method + Detects Single Hangul Consonants as Emoji. -- [[#1505]](https://github.com/GetStream/stream-chat-flutter/issues/1505) Fixed Message bubble disappears for Hangul +- [[#1505]](https://github.com/GetStream/stream-chat-flutter/issues/1505) Fixed Message bubble + disappears for Hangul Consonants. -- [[#1476]](https://github.com/GetStream/stream-chat-flutter/issues/1476) Fixed `UserAvatarTransform.userAvatarBuilder` +- [[#1476]](https://github.com/GetStream/stream-chat-flutter/issues/1476) + Fixed `UserAvatarTransform.userAvatarBuilder` works only for otherUser. -- [[#1490]](https://github.com/GetStream/stream-chat-flutter/issues/1490) Fixed `editMessageInputBuilder` property not +- [[#1490]](https://github.com/GetStream/stream-chat-flutter/issues/1490) + Fixed `editMessageInputBuilder` property not used in message edit widget. -- [[#1523]](https://github.com/GetStream/stream-chat-flutter/issues/1523) Fixed `StreamMessageThemeData` not being +- [[#1523]](https://github.com/GetStream/stream-chat-flutter/issues/1523) + Fixed `StreamMessageThemeData` not being applied correctly. -- [[#1525]](https://github.com/GetStream/stream-chat-flutter/issues/1525) Fixed `StreamQuotedMessageWidget` message for +- [[#1525]](https://github.com/GetStream/stream-chat-flutter/issues/1525) + Fixed `StreamQuotedMessageWidget` message for deleted messages not being shown correctly. -- [[#1529]](https://github.com/GetStream/stream-chat-flutter/issues/1529) Fixed `ClipboardData` requires non-nullable +- [[#1529]](https://github.com/GetStream/stream-chat-flutter/issues/1529) Fixed `ClipboardData` + requires non-nullable string as text on Flutter 3.10. -- [[#1533]](https://github.com/GetStream/stream-chat-flutter/issues/1533) Fixed `StreamMessageListView` messages grouped +- [[#1533]](https://github.com/GetStream/stream-chat-flutter/issues/1533) + Fixed `StreamMessageListView` messages grouped incorrectly w.r.t. timestamp. -- [[#1532]](https://github.com/GetStream/stream-chat-flutter/issues/1532) Fixed `StreamMessageWidget` actions dialog +- [[#1532]](https://github.com/GetStream/stream-chat-flutter/issues/1532) + Fixed `StreamMessageWidget` actions dialog backdrop filter is cut off by safe area. ✅ Added @@ -330,7 +410,8 @@ 🔄 Changed - Updated `dart` sdk environment range to support `3.0.0`. -- Deprecated `MessageTheme.linkBackgroundColor` in favor of `MessageTheme.urlAttachmentBackgroundColor`. +- Deprecated `MessageTheme.linkBackgroundColor` in favor + of `MessageTheme.urlAttachmentBackgroundColor`. - Updated `stream_chat_flutter_core` dependency to [`6.1.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog). @@ -338,18 +419,23 @@ 🐞 Fixed -- [[#1456]](https://github.com/GetStream/stream-chat-flutter/issues/1456) Fixed logic for showing that a message was +- [[#1456]](https://github.com/GetStream/stream-chat-flutter/issues/1456) Fixed logic for showing + that a message was read using sending indicator. -- [[#1462]](https://github.com/GetStream/stream-chat-flutter/issues/1462) Fixed support for iPad in the share button for +- [[#1462]](https://github.com/GetStream/stream-chat-flutter/issues/1462) Fixed support for iPad in + the share button for images. -- [[#1475]](https://github.com/GetStream/stream-chat-flutter/issues/1475) Fixed typo to fix compilation. +- [[#1475]](https://github.com/GetStream/stream-chat-flutter/issues/1475) Fixed typo to fix + compilation. ✅ Added -- Now it is possible to customize the max lines of the title of a url attachment. Before it was always 1 line. +- Now it is possible to customize the max lines of the title of a url attachment. Before it was + always 1 line. - Added `attachmentActionsModalBuilder` parameter to `StreamMessageWidget` that allows to customize `AttachmentActionsModal`. -- Added `StreamMessageInput.sendMessageKeyPredicate` and `StreamMessageInput.clearQuotedMessageKeyPredicate` to +- Added `StreamMessageInput.sendMessageKeyPredicate` + and `StreamMessageInput.clearQuotedMessageKeyPredicate` to customize the keys used to send and clear the quoted message. 🔄 Changed @@ -358,7 +444,8 @@ 🚀 Improved -- Improved draw of reaction options. [#1455](https://github.com/GetStream/stream-chat-flutter/pull/1455) +- Improved draw of reaction + options. [#1455](https://github.com/GetStream/stream-chat-flutter/pull/1455) ## 5.3.0 @@ -368,7 +455,8 @@ 🐞 Fixed -- [[#1424]](https://github.com/GetStream/stream-chat-flutter/issues/1424) Fixed a render issue when showing messages +- [[#1424]](https://github.com/GetStream/stream-chat-flutter/issues/1424) Fixed a render issue when + showing messages starting with 4 whitespaces. - Fixed a bug where the `AttachmentPickerBottomSheet` was not able to identify the mobile browser. - Fixed uploading files on Windows - fixed temp file path. @@ -381,7 +469,8 @@ ✅ Added -- Added a new `bottomRowBuilderWithDefaultWidget` parameter to `StreamMessageWidget` which contains a third parameter ( +- Added a new `bottomRowBuilderWithDefaultWidget` parameter to `StreamMessageWidget` which contains + a third parameter ( default `BottomRow` widget with `copyWith` method available) to allow easier customization. 🔄 Changed @@ -391,18 +480,23 @@ - Updated `connectivity_plus` dependency to `^3.0.2` - Updated `dart_vlc` dependency to `^0.4.0` - Updated `file_picker` dependency to `^5.2.4` -- Deprecated `StreamMessageWidget.bottomRowBuilder` in favor of `StreamMessageWidget.bottomRowBuilderWithDefaultWidget`. +- Deprecated `StreamMessageWidget.bottomRowBuilder` in favor + of `StreamMessageWidget.bottomRowBuilderWithDefaultWidget`. - Deprecated `StreamMessageWidget.deletedBottomRowBuilder` in favor of `StreamMessageWidget.bottomRowBuilderWithDefaultWidget`. -- Deprecated `StreamMessageWidget.usernameBuilder` in favor of `StreamMessageWidget.bottomRowBuilderWithDefaultWidget`. +- Deprecated `StreamMessageWidget.usernameBuilder` in favor + of `StreamMessageWidget.bottomRowBuilderWithDefaultWidget`. 🐞 Fixed -- [[#1379]](https://github.com/GetStream/stream-chat-flutter/issues/1379) Fixed "Issues with photo attachments on web", +- [[#1379]](https://github.com/GetStream/stream-chat-flutter/issues/1379) Fixed "Issues with photo + attachments on web", where the cached image attachment would not render while uploading. -- Fix render overflow issue with `MessageSearchListTileTitle`. It now uses `Text.rich` instead of `Row`. Better default +- Fix render overflow issue with `MessageSearchListTileTitle`. It now uses `Text.rich` instead + of `Row`. Better default behaviour and allows `TextOverflow`. -- [[1346]](https://github.com/GetStream/stream-chat-flutter/issues/1346) Fixed a render issue while uploading video on +- [[1346]](https://github.com/GetStream/stream-chat-flutter/issues/1346) Fixed a render issue while + uploading video on web. - [[#1347]](https://github.com/GetStream/stream-chat-flutter/issues/1347) `onReply` not working in `AttachmentActionsModal` which is used by `StreamImageAttachment` and `StreamImageGroup`. @@ -445,7 +539,8 @@ * `textInputAction` * `keyboardType` * `textCapitalization` -- Added `showStreamAttachmentPickerModalBottomSheet` to show the attachment picker modal bottom sheet. +- Added `showStreamAttachmentPickerModalBottomSheet` to show the attachment picker modal bottom + sheet. 🔄 Changed diff --git a/packages/stream_chat_flutter/example/pubspec.yaml b/packages/stream_chat_flutter/example/pubspec.yaml index 075e8132f..9ec9351c2 100644 --- a/packages/stream_chat_flutter/example/pubspec.yaml +++ b/packages/stream_chat_flutter/example/pubspec.yaml @@ -4,8 +4,8 @@ publish_to: 'none' version: 1.0.0+1 environment: - sdk: '>=2.19.0 <4.0.0' - flutter: ">=3.7.0" + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" dependencies: collection: ^1.15.0 diff --git a/packages/stream_chat_flutter/lib/src/avatars/group_avatar.dart b/packages/stream_chat_flutter/lib/src/avatars/group_avatar.dart index be70bdbff..146a66431 100644 --- a/packages/stream_chat_flutter/lib/src/avatars/group_avatar.dart +++ b/packages/stream_chat_flutter/lib/src/avatars/group_avatar.dart @@ -66,7 +66,8 @@ class StreamGroupAvatar extends StatelessWidget { Widget avatar = GestureDetector( onTap: onTap, child: ClipRRect( - borderRadius: borderRadius ?? previewTheme?.borderRadius, + borderRadius: + borderRadius ?? previewTheme?.borderRadius ?? BorderRadius.zero, child: Container( constraints: constraints ?? previewTheme?.constraints, decoration: BoxDecoration(color: colorTheme.accentPrimary), diff --git a/packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart b/packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart index 72ec020bb..aae50f481 100644 --- a/packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart +++ b/packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart @@ -86,7 +86,8 @@ class StreamUserAvatar extends StatelessWidget { final backupGradientAvatar = ClipRRect( borderRadius: borderRadius ?? - streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius, + streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius ?? + BorderRadius.zero, child: streamChatConfig.defaultUserImage(context, user), ); diff --git a/packages/stream_chat_flutter/lib/src/channel/channel_info.dart b/packages/stream_chat_flutter/lib/src/channel/channel_info.dart index 5c32d2789..abab5ad93 100644 --- a/packages/stream_chat_flutter/lib/src/channel/channel_info.dart +++ b/packages/stream_chat_flutter/lib/src/channel/channel_info.dart @@ -106,9 +106,10 @@ class _ConnectedTitleState extends StatelessWidget { style: textStyle, ); } else { + final lastActive = otherMember.user?.lastActive ?? DateTime.now(); alternativeWidget = Text( '${context.translations.userLastOnlineText} ' - '${Jiffy(otherMember.user?.lastActive).fromNow()}', + '${Jiffy.parseFromDateTime(lastActive).fromNow()}', style: textStyle, ); } diff --git a/packages/stream_chat_flutter/lib/src/channel/channel_preview.dart b/packages/stream_chat_flutter/lib/src/channel/channel_preview.dart index ae6247ea2..f64b9dc95 100644 --- a/packages/stream_chat_flutter/lib/src/channel/channel_preview.dart +++ b/packages/stream_chat_flutter/lib/src/channel/channel_preview.dart @@ -349,16 +349,16 @@ class _Date extends StatelessWidget { if (lastMessageAt.millisecondsSinceEpoch >= startOfDay.millisecondsSinceEpoch) { - stringDate = Jiffy(lastMessageAt.toLocal()).jm; + stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).jm; } else if (lastMessageAt.millisecondsSinceEpoch >= startOfDay .subtract(const Duration(days: 1)) .millisecondsSinceEpoch) { stringDate = context.translations.yesterdayLabel; } else if (startOfDay.difference(lastMessageAt).inDays < 7) { - stringDate = Jiffy(lastMessageAt.toLocal()).EEEE; + stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).EEEE; } else { - stringDate = Jiffy(lastMessageAt.toLocal()).yMd; + stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).yMd; } return Text( diff --git a/packages/stream_chat_flutter/lib/src/channel/stream_channel_avatar.dart b/packages/stream_chat_flutter/lib/src/channel/stream_channel_avatar.dart index 9090c33a0..8f148a370 100644 --- a/packages/stream_chat_flutter/lib/src/channel/stream_channel_avatar.dart +++ b/packages/stream_chat_flutter/lib/src/channel/stream_channel_avatar.dart @@ -111,7 +111,8 @@ class StreamChannelAvatar extends StatelessWidget { initialData: channel.image, builder: (context, channelImage) { Widget child = ClipRRect( - borderRadius: borderRadius ?? previewTheme?.borderRadius, + borderRadius: + borderRadius ?? previewTheme?.borderRadius ?? BorderRadius.zero, child: Container( constraints: constraints ?? previewTheme?.constraints, decoration: BoxDecoration(color: colorTheme.accentPrimary), diff --git a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart index 2a791954f..a1708e17f 100644 --- a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart +++ b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart @@ -3,13 +3,11 @@ import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:chewie/chewie.dart'; -import 'package:contextmenu/contextmenu.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:photo_view/photo_view.dart'; import 'package:shimmer/shimmer.dart'; import 'package:stream_chat_flutter/platform_widget_builder/platform_widget_builder.dart'; -import 'package:stream_chat_flutter/src/context_menu_items/download_menu_item.dart'; import 'package:stream_chat_flutter/src/fullscreen_media/full_screen_media_widget.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:video_player/video_player.dart'; @@ -281,84 +279,83 @@ class _FullScreenMediaState extends State { final currentAttachmentPackage = widget.mediaAttachmentPackages[index]; final attachment = currentAttachmentPackage.attachment; - if (attachment.type == 'image' || attachment.type == 'giphy') { - final imageUrl = attachment.imageUrl ?? - attachment.assetUrl ?? - attachment.thumbUrl; - return ValueListenableBuilder( - valueListenable: _isDisplayingDetail, - builder: (context, isDisplayingDetail, _) => - AnimatedContainer( + return ValueListenableBuilder( + valueListenable: _isDisplayingDetail, + builder: (context, isDisplayingDetail, child) { + return AnimatedContainer( + duration: kThemeChangeDuration, color: isDisplayingDetail ? StreamChannelHeaderTheme.of(context).color : Colors.black, - duration: kThemeAnimationDuration, - child: ContextMenuArea( - verticalPadding: 0, - builder: (_) => [ - DownloadMenuItem( - attachment: attachment, - ), - ], - child: PhotoView( - imageProvider: (imageUrl == null && - attachment.localUri != null && - attachment.file?.bytes != null) - ? Image.memory(attachment.file!.bytes!).image - : CachedNetworkImageProvider(imageUrl!), - errorBuilder: (_, __, ___) => const AttachmentError(), - loadingBuilder: (context, _) { - final image = Image.asset( - 'images/placeholder.png', - fit: BoxFit.cover, - package: 'stream_chat_flutter', + child: Builder( + builder: (context) { + if (attachment.type == 'image' || + attachment.type == 'giphy') { + final imageUrl = attachment.imageUrl ?? + attachment.assetUrl ?? + attachment.thumbUrl; + + return PhotoView( + imageProvider: (imageUrl == null && + attachment.localUri != null && + attachment.file?.bytes != null) + ? Image.memory(attachment.file!.bytes!).image + : CachedNetworkImageProvider(imageUrl!), + errorBuilder: (_, __, ___) => + const AttachmentError(), + loadingBuilder: (context, _) { + final image = Image.asset( + 'images/placeholder.png', + fit: BoxFit.cover, + package: 'stream_chat_flutter', + ); + final colorTheme = + StreamChatTheme.of(context).colorTheme; + return Shimmer.fromColors( + baseColor: colorTheme.disabled, + highlightColor: colorTheme.inputBg, + child: image, + ); + }, + maxScale: PhotoViewComputedScale.covered, + minScale: PhotoViewComputedScale.contained, + heroAttributes: PhotoViewHeroAttributes( + tag: widget.mediaAttachmentPackages, + ), + backgroundDecoration: const BoxDecoration( + color: Colors.transparent, + ), ); - final colorTheme = - StreamChatTheme.of(context).colorTheme; - return Shimmer.fromColors( - baseColor: colorTheme.disabled, - highlightColor: colorTheme.inputBg, - child: image, + } else if (attachment.type == 'video') { + final controller = videoPackages[attachment.id]!; + if (!controller.initialized) { + return const Center( + child: CircularProgressIndicator.adaptive(), + ); + } + + final mediaQuery = MediaQuery.of(context); + final bottomPadding = mediaQuery.padding.bottom; + + return AnimatedPadding( + duration: kThemeChangeDuration, + padding: EdgeInsets.symmetric( + vertical: isDisplayingDetail + ? kToolbarHeight + bottomPadding + : 0, + ), + child: Chewie( + controller: controller.chewieController!, + ), ); - }, - maxScale: PhotoViewComputedScale.covered, - minScale: PhotoViewComputedScale.contained, - heroAttributes: PhotoViewHeroAttributes( - tag: widget.mediaAttachmentPackages, - ), - backgroundDecoration: const BoxDecoration( - color: Colors.transparent, - ), - ), + } + + return const SizedBox(); + }, ), - ), - ); - } else if (attachment.type == 'video') { - final controller = videoPackages[attachment.id]!; - if (!controller.initialized) { - return const Center( - child: CircularProgressIndicator.adaptive(), ); - } - return InkWell( - onTap: switchDisplayingDetail, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 50), - child: ContextMenuArea( - verticalPadding: 0, - builder: (_) => [ - DownloadMenuItem( - attachment: attachment, - ), - ], - child: Chewie( - controller: controller.chewieController!, - ), - ), - ), - ); - } - return const SizedBox(); + }, + ); }, ), ), @@ -475,6 +472,7 @@ class VideoPackage { videoPlayerController: _videoPlayerController, autoInitialize: _autoInitialize, showControls: _showControls, + showOptions: false, aspectRatio: _videoPlayerController.value.aspectRatio, ); }); diff --git a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_desktop.dart b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_desktop.dart index 13185a794..0b870f773 100644 --- a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_desktop.dart +++ b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_desktop.dart @@ -307,88 +307,95 @@ class _FullScreenMediaDesktopState extends State { final currentAttachmentPackage = widget.mediaAttachmentPackages[index]; final attachment = currentAttachmentPackage.attachment; - if (attachment.type == 'image' || attachment.type == 'giphy') { - final imageUrl = attachment.imageUrl ?? - attachment.assetUrl ?? - attachment.thumbUrl; - return ValueListenableBuilder( - valueListenable: _isDisplayingDetail, - builder: (context, isDisplayingDetail, _) => - AnimatedContainer( + + return ValueListenableBuilder( + valueListenable: _isDisplayingDetail, + builder: (context, isDisplayingDetail, child) { + return AnimatedContainer( + duration: kThemeChangeDuration, color: isDisplayingDetail ? StreamChannelHeaderTheme.of(context).color : Colors.black, - duration: kThemeAnimationDuration, - child: ContextMenuArea( - verticalPadding: 0, - builder: (_) => [ - DownloadMenuItem( - attachment: attachment, - ), - ], - child: PhotoView( - imageProvider: (imageUrl == null && - attachment.localUri != null && - attachment.file?.bytes != null) - ? Image.memory(attachment.file!.bytes!).image - : CachedNetworkImageProvider(imageUrl!), - errorBuilder: (_, __, ___) => const AttachmentError(), - loadingBuilder: (context, _) { - final image = Image.asset( - 'images/placeholder.png', - fit: BoxFit.cover, - package: 'stream_chat_flutter', + child: Builder( + builder: (context) { + if (attachment.type == 'image' || + attachment.type == 'giphy') { + final imageUrl = attachment.imageUrl ?? + attachment.assetUrl ?? + attachment.thumbUrl; + + return PhotoView( + imageProvider: (imageUrl == null && + attachment.localUri != null && + attachment.file?.bytes != null) + ? Image.memory(attachment.file!.bytes!).image + : CachedNetworkImageProvider(imageUrl!), + errorBuilder: (_, __, ___) => + const AttachmentError(), + loadingBuilder: (context, _) { + final image = Image.asset( + 'images/placeholder.png', + fit: BoxFit.cover, + package: 'stream_chat_flutter', + ); + final colorTheme = + StreamChatTheme.of(context).colorTheme; + return Shimmer.fromColors( + baseColor: colorTheme.disabled, + highlightColor: colorTheme.inputBg, + child: image, + ); + }, + maxScale: PhotoViewComputedScale.covered, + minScale: PhotoViewComputedScale.contained, + heroAttributes: PhotoViewHeroAttributes( + tag: widget.mediaAttachmentPackages, + ), + backgroundDecoration: const BoxDecoration( + color: Colors.transparent, + ), ); - final colorTheme = - StreamChatTheme.of(context).colorTheme; - return Shimmer.fromColors( - baseColor: colorTheme.disabled, - highlightColor: colorTheme.inputBg, - child: image, + } else if (attachment.type == 'video') { + final package = videoPackages[attachment.id]!; + package.player.open( + Playlist( + medias: [ + Media.network(package.attachment.assetUrl), + ], + ), + autoStart: widget.autoplayVideos, ); - }, - maxScale: PhotoViewComputedScale.covered, - minScale: PhotoViewComputedScale.contained, - heroAttributes: PhotoViewHeroAttributes( - tag: widget.mediaAttachmentPackages, - ), - backgroundDecoration: const BoxDecoration( - color: Colors.transparent, - ), - ), - ), - ), - ); - } else if (attachment.type == 'video') { - final package = videoPackages[attachment.id]!; - package.player.open( - Playlist( - medias: [ - Media.network(package.attachment.assetUrl), - ], - ), - autoStart: widget.autoplayVideos, - ); - return InkWell( - onTap: switchDisplayingDetail, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 50), - child: ContextMenuArea( - verticalPadding: 0, - builder: (_) => [ - DownloadMenuItem( - attachment: attachment, - ), - ], - child: Video( - player: package.player, - ), + final mediaQuery = MediaQuery.of(context); + final bottomPadding = mediaQuery.padding.bottom; + + return AnimatedPadding( + duration: kThemeChangeDuration, + padding: EdgeInsets.symmetric( + vertical: isDisplayingDetail + ? kToolbarHeight + bottomPadding + : 0, + ), + child: ContextMenuArea( + verticalPadding: 0, + builder: (_) => [ + DownloadMenuItem( + attachment: attachment, + ), + ], + child: Video( + player: package.player, + ), + ), + ); + } + + return const SizedBox(); + }, ), - ), - ); - } - return const SizedBox(); + ); + }, + ); }, ), ), diff --git a/packages/stream_chat_flutter/lib/src/localization/translations.dart b/packages/stream_chat_flutter/lib/src/localization/translations.dart index 61c3441a2..e6bb87d97 100644 --- a/packages/stream_chat_flutter/lib/src/localization/translations.dart +++ b/packages/stream_chat_flutter/lib/src/localization/translations.dart @@ -611,13 +611,15 @@ class DefaultTranslations implements Translations { } else if (date == yesterday) { return 'yesterday'; } else { - return 'on ${Jiffy(date).MMMd}'; + return 'on ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - 'Sent ${_getDay(date)} at ${Jiffy(time.toLocal()).format('HH:mm')}'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Sent ${_getDay(date)} at ${atTime.jm}'; + } @override String get todayLabel => 'Today'; diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/stream_attachment_picker.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/stream_attachment_picker.dart index 6c8f1f59c..94a814129 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/stream_attachment_picker.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/stream_attachment_picker.dart @@ -698,6 +698,7 @@ Widget mobileAttachmentPickerBuilder({ ThumbnailFormat attachmentThumbnailFormat = ThumbnailFormat.jpeg, int attachmentThumbnailQuality = 100, double attachmentThumbnailScale = 1, + ErrorListener? onError, }) { return StreamMobileAttachmentPickerBottomSheet( controller: controller, @@ -721,10 +722,15 @@ Widget mobileAttachmentPickerBuilder({ mediaThumbnailQuality: attachmentThumbnailQuality, mediaThumbnailScale: attachmentThumbnailScale, onMediaItemSelected: (media) async { - if (selectedIds.contains(media.id)) { - return controller.removeAssetAttachment(media); + try { + if (selectedIds.contains(media.id)) { + return await controller.removeAssetAttachment(media); + } + return await controller.addAssetAttachment(media); + } catch (e, stk) { + if (onError != null) return onError.call(e, stk); + rethrow; } - return controller.addAssetAttachment(media); }, ); }, @@ -736,8 +742,15 @@ Widget mobileAttachmentPickerBuilder({ optionViewBuilder: (context, controller) { return StreamFilePicker( onFilePicked: (file) async { - if (file != null) await controller.addAttachment(file); - return Navigator.pop(context, controller.value); + try { + if (file != null) await controller.addAttachment(file); + return Navigator.pop(context, controller.value); + } catch (e, stk) { + Navigator.pop(context, controller.value); + if (onError != null) return onError.call(e, stk); + + rethrow; + } }, ); }, @@ -749,10 +762,17 @@ Widget mobileAttachmentPickerBuilder({ optionViewBuilder: (context, controller) { return StreamImagePicker( onImagePicked: (image) async { - if (image != null) { - await controller.addAttachment(image); + try { + if (image != null) { + await controller.addAttachment(image); + } + return Navigator.pop(context, controller.value); + } catch (e, stk) { + Navigator.pop(context, controller.value); + if (onError != null) return onError.call(e, stk); + + rethrow; } - return Navigator.pop(context, controller.value); }, ); }, @@ -764,10 +784,17 @@ Widget mobileAttachmentPickerBuilder({ optionViewBuilder: (context, controller) { return StreamVideoPicker( onVideoPicked: (video) async { - if (video != null) { - await controller.addAttachment(video); + try { + if (video != null) { + await controller.addAttachment(video); + } + return Navigator.pop(context, controller.value); + } catch (e, stk) { + Navigator.pop(context, controller.value); + if (onError != null) return onError.call(e, stk); + + rethrow; } - return Navigator.pop(context, controller.value); }, ); }, @@ -787,6 +814,7 @@ Widget webOrDesktopAttachmentPickerBuilder({ ThumbnailFormat attachmentThumbnailFormat = ThumbnailFormat.jpeg, int attachmentThumbnailQuality = 100, double attachmentThumbnailScale = 1, + ErrorListener? onError, }) { return StreamWebOrDesktopAttachmentPickerBottomSheet( controller: controller, @@ -814,13 +842,20 @@ Widget webOrDesktopAttachmentPickerBuilder({ }.where((option) => option.supportedTypes.every(allowedTypes.contains)), }, onOptionTap: (context, controller, option) async { - final attachment = await StreamAttachmentHandler.instance.pickFile( - type: option.type.fileType, - ); - if (attachment != null) { - await controller.addAttachment(attachment); + try { + final attachment = await StreamAttachmentHandler.instance.pickFile( + type: option.type.fileType, + ); + if (attachment != null) { + await controller.addAttachment(attachment); + } + return Navigator.pop(context, controller.value); + } catch (e, stk) { + Navigator.pop(context, controller.value); + if (onError != null) return onError.call(e, stk); + + rethrow; } - return Navigator.pop(context, controller.value); }, ); } diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/stream_attachment_picker_bottom_sheet.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/stream_attachment_picker_bottom_sheet.dart index bc53e4db2..4546bce60 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/stream_attachment_picker_bottom_sheet.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/stream_attachment_picker_bottom_sheet.dart @@ -69,6 +69,7 @@ Future showStreamAttachmentPickerModalBottomSheet({ List allowedTypes = AttachmentPickerType.values, List? initialAttachments, StreamAttachmentPickerController? controller, + ErrorListener? onError, Color? backgroundColor, double? elevation, BoxConstraints? constraints, @@ -117,6 +118,7 @@ Future showStreamAttachmentPickerModalBottomSheet({ if (isWebOrDesktop) { return webOrDesktopAttachmentPickerBuilder.call( context: context, + onError: onError, controller: controller, allowedTypes: allowedTypes, customOptions: customOptions?.map( @@ -131,6 +133,7 @@ Future showStreamAttachmentPickerModalBottomSheet({ return mobileAttachmentPickerBuilder.call( context: context, + onError: onError, controller: controller, allowedTypes: allowedTypes, customOptions: customOptions, diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart index ab36c3be3..227a5192c 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart @@ -147,6 +147,7 @@ class StreamMessageInput extends StatefulWidget { _defaultClearQuotedMessageKeyPredicate, this.ogPreviewFilter = _defaultOgPreviewFilter, this.hintGetter = _defaultHintGetter, + this.contentInsertionConfiguration, }); /// The predicate used to send a message on desktop/web @@ -340,6 +341,9 @@ class StreamMessageInput extends StatefulWidget { /// Returns the hint text for the message input. final HintGetter hintGetter; + /// {@macro flutter.widgets.editableText.contentInsertionConfiguration} + final ContentInsertionConfiguration? contentInsertionConfiguration; + static String? _defaultHintGetter( BuildContext context, HintType type, @@ -815,6 +819,7 @@ class StreamMessageInputState extends State Future _onAttachmentButtonPressed() async { final attachments = await showStreamAttachmentPickerModalBottomSheet( context: context, + onError: widget.onError, allowedTypes: widget.allowedAttachmentPickerTypes, initialAttachments: _effectiveController.attachments, ); @@ -904,6 +909,8 @@ class StreamMessageInputState extends State decoration: _getInputDecoration(context), textCapitalization: widget.textCapitalization, autocorrect: widget.autoCorrect, + contentInsertionConfiguration: + widget.contentInsertionConfiguration, ), ), ), diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_text_field.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_text_field.dart index a04ab829d..292c9141f 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_text_field.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/stream_message_text_field.dart @@ -120,6 +120,7 @@ class StreamMessageTextField extends StatefulWidget { this.restorationId, this.scribbleEnabled = true, this.enableIMEPersonalizedLearning = true, + this.contentInsertionConfiguration, }) : assert(obscuringCharacter.length == 1, ''), smartDashesType = smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled), @@ -526,6 +527,9 @@ class StreamMessageTextField extends StatefulWidget { /// {@macro flutter.services.TextInputConfiguration.enableIMEPersonalizedLearning} final bool enableIMEPersonalizedLearning; + /// {@macro flutter.widgets.editableText.contentInsertionConfiguration} + final ContentInsertionConfiguration? contentInsertionConfiguration; + @override _StreamMessageTextFieldState createState() => _StreamMessageTextFieldState(); @@ -622,6 +626,9 @@ class StreamMessageTextField extends StatefulWidget { properties.add(DiagnosticsProperty( 'enableIMEPersonalizedLearning', enableIMEPersonalizedLearning, defaultValue: true)); + properties.add(DiagnosticsProperty( + 'contentInsertionConfiguration', contentInsertionConfiguration, + defaultValue: null)); } } @@ -727,6 +734,7 @@ class _StreamMessageTextFieldState extends State restorationId: widget.restorationId, scribbleEnabled: widget.scribbleEnabled, enableIMEPersonalizedLearning: widget.enableIMEPersonalizedLearning, + contentInsertionConfiguration: widget.contentInsertionConfiguration, ); @override diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart index 5a5075f88..f9de17876 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart @@ -669,14 +669,20 @@ class _StreamMessageListViewState extends State { final isPartOfThread = message.replyCount! > 0 || message.showInChannel == true; - final createdAt = message.createdAt.toLocal(); - final nextCreatedAt = nextMessage.createdAt.toLocal(); - if (!Jiffy(createdAt).isSame(nextCreatedAt, Units.DAY)) { + final createdAt = Jiffy.parseFromDateTime( + message.createdAt.toLocal(), + ); + + final nextCreatedAt = Jiffy.parseFromDateTime( + nextMessage.createdAt.toLocal(), + ); + + if (!createdAt.isSame(nextCreatedAt, unit: Unit.day)) { separator = _buildDateDivider(nextMessage); } else { - final hasTimeDiff = !Jiffy(createdAt).isSame( + final hasTimeDiff = !createdAt.isSame( nextCreatedAt, - Units.MINUTE, + unit: Unit.minute, ); final isNextUserSame = @@ -1071,10 +1077,12 @@ class _StreamMessageListViewState extends State { var hasTimeDiff = false; if (nextMessage != null) { - hasTimeDiff = !Jiffy(message.createdAt.toLocal()).isSame( + final createdAt = Jiffy.parseFromDateTime(message.createdAt.toLocal()); + final nextCreatedAt = Jiffy.parseFromDateTime( nextMessage.createdAt.toLocal(), - Units.MINUTE, ); + + hasTimeDiff = !createdAt.isSame(nextCreatedAt, unit: Unit.minute); } final hasFileAttachment = diff --git a/packages/stream_chat_flutter/lib/src/message_widget/bottom_row.dart b/packages/stream_chat_flutter/lib/src/message_widget/bottom_row.dart index 5ca2462a5..83a40a4d5 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/bottom_row.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/bottom_row.dart @@ -162,8 +162,7 @@ class BottomRow extends StatelessWidget { msg = context.translations.threadReplyCountText(replyCount); } - // ignore: prefer_function_declarations_over_variables - final _onThreadTap = () async { + Future _onThreadTap() async { try { var message = this.message; if (showInChannel) { @@ -172,12 +171,9 @@ class BottomRow extends StatelessWidget { } return onThreadTap!(message); } catch (e, stk) { - print(e); - print(stk); - // ignore: avoid_returning_null_for_void - return null; + debugPrint('Error while fetching message: $e, $stk'); } - }; + } const usernameKey = Key('username'); @@ -191,7 +187,7 @@ class BottomRow extends StatelessWidget { ), if (showTimeStamp) Text( - Jiffy(message.createdAt.toLocal()).jm, + Jiffy.parseFromDateTime(message.createdAt.toLocal()).jm, style: messageTheme.createdAtStyle, ), if (showSendingIndicator) diff --git a/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart index f62adf841..f99ee144d 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart @@ -55,6 +55,7 @@ class StreamMessageWidget extends StatefulWidget { this.attachmentBorderRadiusGeometry, this.onMentionTap, this.onMessageTap, + this.onReactionsTap, bool? showReactionPicker, @Deprecated('Use `showReactionPicker` instead') bool showReactionPickerIndicator = true, @@ -563,6 +564,9 @@ class StreamMessageWidget extends StatefulWidget { /// {@macro onMessageTap} final void Function(Message)? onMessageTap; + /// {@macro onReactionsTap} + final OnReactionsTap? onReactionsTap; + /// {@template customActions} /// List of custom actions shown on message long tap /// {@endtemplate} @@ -657,6 +661,7 @@ class StreamMessageWidget extends StatefulWidget { bool? translateUserAvatar, OnQuotedMessageTap? onQuotedMessageTap, void Function(Message)? onMessageTap, + OnReactionsTap? onReactionsTap, List? customActions, void Function(Message message, Attachment attachment)? onAttachmentTap, Widget Function(BuildContext, User)? userAvatarBuilder, @@ -746,6 +751,7 @@ class StreamMessageWidget extends StatefulWidget { translateUserAvatar: translateUserAvatar ?? this.translateUserAvatar, onQuotedMessageTap: onQuotedMessageTap ?? this.onQuotedMessageTap, onMessageTap: onMessageTap ?? this.onMessageTap, + onReactionsTap: onReactionsTap ?? this.onReactionsTap, customActions: customActions ?? this.customActions, onAttachmentTap: onAttachmentTap ?? this.onAttachmentTap, userAvatarBuilder: userAvatarBuilder ?? this.userAvatarBuilder, @@ -986,7 +992,11 @@ class _StreamMessageWidgetState extends State showPinHighlight: widget.showPinHighlight, showReactionPickerTail: widget.showReactionPickerTail, showReactions: showReactions, - onReactionsTap: () => _showMessageReactionsModal(context), + onReactionsTap: () { + widget.onReactionsTap != null + ? widget.onReactionsTap!(widget.message) + : _showMessageReactionsModal(context); + }, showUserAvatar: widget.showUserAvatar, streamChat: _streamChat, translateUserAvatar: widget.translateUserAvatar, diff --git a/packages/stream_chat_flutter/lib/src/misc/date_divider.dart b/packages/stream_chat_flutter/lib/src/misc/date_divider.dart index 24af4f3f2..24c0c9a73 100644 --- a/packages/stream_chat_flutter/lib/src/misc/date_divider.dart +++ b/packages/stream_chat_flutter/lib/src/misc/date_divider.dart @@ -20,17 +20,17 @@ class StreamDateDivider extends StatelessWidget { @override Widget build(BuildContext context) { - final createdAt = Jiffy(dateTime); - final now = Jiffy(DateTime.now()); + final createdAt = Jiffy.parseFromDateTime(dateTime); + final now = Jiffy.parseFromDateTime(DateTime.now()); var dayInfo = createdAt.MMMd; - if (createdAt.isSame(now, Units.DAY)) { + if (createdAt.isSame(now, unit: Unit.day)) { dayInfo = context.translations.todayLabel; - } else if (createdAt.isSame(now.subtract(days: 1), Units.DAY)) { + } else if (createdAt.isSame(now.subtract(days: 1), unit: Unit.day)) { dayInfo = context.translations.yesterdayLabel; - } else if (createdAt.isAfter(now.subtract(days: 7), Units.DAY)) { + } else if (createdAt.isAfter(now.subtract(days: 7), unit: Unit.day)) { dayInfo = createdAt.EEEE; - } else if (createdAt.isAfter(now.subtract(years: 1), Units.DAY)) { + } else if (createdAt.isAfter(now.subtract(years: 1), unit: Unit.day)) { dayInfo = createdAt.MMMd; } diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_tile.dart index 39951d53a..b1b165ba2 100644 --- a/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_tile.dart +++ b/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_tile.dart @@ -287,16 +287,16 @@ class ChannelLastMessageDate extends StatelessWidget { if (lastMessageAt.millisecondsSinceEpoch >= startOfDay.millisecondsSinceEpoch) { - stringDate = Jiffy(lastMessageAt.toLocal()).jm; + stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).jm; } else if (lastMessageAt.millisecondsSinceEpoch >= startOfDay .subtract(const Duration(days: 1)) .millisecondsSinceEpoch) { stringDate = context.translations.yesterdayLabel; } else if (startOfDay.difference(lastMessageAt).inDays < 7) { - stringDate = Jiffy(lastMessageAt.toLocal()).EEEE; + stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).EEEE; } else { - stringDate = Jiffy(lastMessageAt.toLocal()).yMd; + stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).yMd; } return Text( diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_tile.dart index 5aab3b2d4..3eb03aec1 100644 --- a/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_tile.dart +++ b/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_tile.dart @@ -227,9 +227,9 @@ class MessageSearchTileMessageDate extends StatelessWidget { if (now.year != createdAt.year || now.month != createdAt.month || now.day != createdAt.day) { - stringDate = Jiffy(createdAt.toLocal()).yMd; + stringDate = Jiffy.parseFromDateTime(createdAt.toLocal()).yMd; } else { - stringDate = Jiffy(createdAt.toLocal()).jm; + stringDate = Jiffy.parseFromDateTime(createdAt.toLocal()).jm; } return Text( diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery_tile.dart index 2f03613c8..b93686d0b 100644 --- a/packages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery_tile.dart +++ b/packages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery_tile.dart @@ -202,10 +202,9 @@ class MediaThumbnailProvider extends ImageProvider { } @override - @Deprecated('Will get replaced by loadImage in the next major version.') - ImageStreamCompleter loadBuffer( + ImageStreamCompleter loadImage( MediaThumbnailProvider key, - DecoderBufferCallback decode, + ImageDecoderCallback decode, ) { return MultiFrameImageStreamCompleter( codec: _loadAsync(key, decode), @@ -220,10 +219,9 @@ class MediaThumbnailProvider extends ImageProvider { ); } - @Deprecated('Will get replaced by loadImage in the next major version.') Future _loadAsync( MediaThumbnailProvider key, - DecoderBufferCallback decode, + ImageDecoderCallback decode, ) async { assert(key == this, '$key is not $this'); final bytes = await media.thumbnailDataWithSize( diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_tile.dart index 5ee49dccb..1b0251f6a 100644 --- a/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_tile.dart +++ b/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_tile.dart @@ -176,11 +176,12 @@ class UserLastActive extends StatelessWidget { @override Widget build(BuildContext context) { final chatTheme = StreamChatTheme.of(context); + final lastActive = user.lastActive ?? DateTime.now(); return Text( user.online ? context.translations.userOnlineText : '${context.translations.userLastOnlineText} ' - '${Jiffy(user.lastActive).fromNow()}', + '${Jiffy.parseFromDateTime(lastActive).fromNow()}', style: chatTheme.textTheme.footnote.copyWith( color: chatTheme.colorTheme.textHighEmphasis.withOpacity(0.5), ), diff --git a/packages/stream_chat_flutter/lib/src/stream_chat.dart b/packages/stream_chat_flutter/lib/src/stream_chat.dart index 73a0d0a0a..6b0d0e609 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat.dart @@ -165,9 +165,9 @@ class StreamChatState extends State { @override void didChangeDependencies() { final currentLocale = Localizations.localeOf(context).toString(); - final availableLocales = Jiffy.getAllAvailableLocales(); + final availableLocales = Jiffy.getSupportedLocales(); if (availableLocales.contains(currentLocale)) { - Jiffy.locale(currentLocale); + Jiffy.setLocale(currentLocale); } super.didChangeDependencies(); } diff --git a/packages/stream_chat_flutter/lib/src/user/user_item.dart b/packages/stream_chat_flutter/lib/src/user/user_item.dart index 019322d9b..0c13d95ec 100644 --- a/packages/stream_chat_flutter/lib/src/user/user_item.dart +++ b/packages/stream_chat_flutter/lib/src/user/user_item.dart @@ -74,11 +74,12 @@ class StreamUserItem extends StatelessWidget { Widget _buildLastActive(BuildContext context) { final chatTheme = StreamChatTheme.of(context); + final lastActive = user.lastActive ?? DateTime.now(); return Text( user.online ? context.translations.userOnlineText : '${context.translations.userLastOnlineText} ' - '${Jiffy(user.lastActive).fromNow()}', + '${Jiffy.parseFromDateTime(lastActive).fromNow()}', style: chatTheme.textTheme.footnote.copyWith( color: chatTheme.colorTheme.textHighEmphasis.withOpacity(0.5), ), diff --git a/packages/stream_chat_flutter/lib/src/utils/extensions.dart b/packages/stream_chat_flutter/lib/src/utils/extensions.dart index 06b02aae1..94a1bd838 100644 --- a/packages/stream_chat_flutter/lib/src/utils/extensions.dart +++ b/packages/stream_chat_flutter/lib/src/utils/extensions.dart @@ -352,12 +352,12 @@ extension MessageX on Message { final userName = user.name; if (linkify) { messageTextToRender = messageTextToRender?.replaceAll( - '@$userId', - '[@$userName](@${userName.replaceAll(' ', '')})', + RegExp('@($userId|$userName)'), + '[@$userName]($userId)', ); } else { messageTextToRender = messageTextToRender?.replaceAll( - '@$userId', + RegExp('@($userId|$userName)'), '@$userName', ); } diff --git a/packages/stream_chat_flutter/lib/src/utils/typedefs.dart b/packages/stream_chat_flutter/lib/src/utils/typedefs.dart index 64f18e1ad..f278de0c0 100644 --- a/packages/stream_chat_flutter/lib/src/utils/typedefs.dart +++ b/packages/stream_chat_flutter/lib/src/utils/typedefs.dart @@ -230,6 +230,11 @@ typedef OnQuotedMessageTap = void Function(String?); /// {@endtemplate} typedef OnMessageTap = void Function(Message); +/// {@template onReactionsTap} +/// The action to perform when a message's reactions are tapped. +/// {@endtemplate} +typedef OnReactionsTap = void Function(Message); + /// {@template messageSearchItemTapCallback} /// The action to perform when tapping or clicking on a user in a // ignore: deprecated_member_use_from_same_package diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 4921cff8d..b2978b52b 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -1,47 +1,47 @@ name: 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. -version: 6.8.1 +version: 6.9.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues environment: - sdk: ">=2.19.0 <4.0.0" - flutter: ">=3.7.0" + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" dependencies: cached_network_image: ^3.2.3 chewie: ^1.7.0 - collection: ^1.17.0 + collection: ^1.17.1 contextmenu: ^3.0.0 dart_vlc: ^0.4.0 desktop_drop: ^0.4.1 diacritic: ^0.1.4 - dio: ^5.2.1+1 + dio: ^5.3.2 ezanimation: ^0.6.0 - file_picker: ^5.3.1 + file_picker: ^5.3.3 file_selector: ^1.0.0 flutter: sdk: flutter - flutter_markdown: ^0.6.15 + flutter_markdown: ^0.6.17+1 flutter_portal: ^1.1.4 - flutter_svg: ^2.0.5 + flutter_svg: ^2.0.7 http_parser: ^4.0.2 image_gallery_saver: ^2.0.3 - image_picker: ^1.0.0 - jiffy: ^5.0.0 - lottie: ^2.3.2 - meta: ^1.8.0 - path_provider: ^2.0.15 - photo_manager: ^2.6.0 + image_picker: ^1.0.2 + jiffy: ^6.2.1 + lottie: ^2.6.0 + meta: ^1.9.1 + path_provider: ^2.1.0 + photo_manager: ^2.7.1 photo_view: ^0.14.0 rxdart: ^0.27.7 - share_plus: ^7.0.2 + share_plus: ^7.1.0 shimmer: ^3.0.0 - stream_chat_flutter_core: ^6.7.0 + stream_chat_flutter_core: ^6.8.0 synchronized: ^3.1.0 thumblr: ^0.0.4 - url_launcher: ^6.1.11 + url_launcher: ^6.1.12 video_player: ^2.7.0 video_player_macos: ^2.0.1 video_thumbnail: ^0.5.3 @@ -73,5 +73,5 @@ dev_dependencies: flutter_test: sdk: flutter golden_toolkit: ^0.15.0 - mocktail: ^0.3.0 - path: ^1.8.2 \ No newline at end of file + mocktail: ^1.0.0 + path: ^1.8.3 \ No newline at end of file diff --git a/packages/stream_chat_flutter/test/src/utils/extension_test.dart b/packages/stream_chat_flutter/test/src/utils/extension_test.dart index eef7677e1..52a6cc5f5 100644 --- a/packages/stream_chat_flutter/test/src/utils/extension_test.dart +++ b/packages/stream_chat_flutter/test/src/utils/extension_test.dart @@ -148,4 +148,126 @@ void main() { expect('ㅎㅎㅎㅎ'.isOnlyEmoji, false); }); }); + + group('Message Extension Tests', () { + test('replaceMentions should replace user mentions with names and IDs', () { + final user1 = User(id: 'user1', name: 'Alice'); + final user2 = User(id: 'user2', name: 'Bob'); + + final message = Message( + text: 'Hello, @user1 and @user2!', + mentionedUsers: [user1, user2], + ); + + final modifiedMessage = message.replaceMentions(); + + expect(modifiedMessage.text, contains('[@Alice](user1)')); + expect(modifiedMessage.text, contains('[@Bob](user2)')); + }); + + test('replaceMentions without linkify should not add links', () { + final user = User(id: 'user1', name: 'Alice'); + + final message = Message( + text: 'Hello, @user1!', + mentionedUsers: [user], + ); + + final modifiedMessage = message.replaceMentions(linkify: false); + + expect(modifiedMessage.text, contains('@Alice')); + }); + + test('replaceMentions should handle mentions with usernames', () { + final user = User(id: 'user1', name: 'Alice'); + + final message = Message( + text: 'Hello, @Alice!', + mentionedUsers: [user], + ); + + final modifiedMessage = message.replaceMentions(); + + expect(modifiedMessage.text, contains('[@Alice](user1)')); + }); + + test( + '''replaceMentions without linkify should not change mentions with usernames''', + () { + final user = User(id: 'user1', name: 'Alice'); + + final message = Message( + text: 'Hello, @Alice!', + mentionedUsers: [user], + ); + + final modifiedMessage = message.replaceMentions(linkify: false); + + expect(modifiedMessage.text, contains('@Alice')); + }, + ); + + test( + 'replaceMentions should replace mixed user mentions with names and IDs', + () { + final user1 = User(id: 'user1', name: 'Alice'); + final user2 = User(id: 'user2', name: 'Bob'); + + final message = Message( + text: 'Hello, @user1 and @Bob!', + mentionedUsers: [user1, user2], + ); + + final modifiedMessage = message.replaceMentions(); + + expect(modifiedMessage.text, contains('[@Alice](user1)')); + expect(modifiedMessage.text, contains('[@Bob](user2)')); + }, + ); + + test('replaceMentionsWithId should replace user names with IDs', () { + final user1 = User(id: 'user1', name: 'Alice'); + final user2 = User(id: 'user2', name: 'Bob'); + + final message = Message( + text: 'Hello, @Alice and @Bob!', + mentionedUsers: [user1, user2], + ); + + final modifiedMessage = message.replaceMentionsWithId(); + + expect(modifiedMessage.text, contains('@user1')); + expect(modifiedMessage.text, contains('@user2')); + expect(modifiedMessage.text, isNot(contains('@Alice'))); + expect(modifiedMessage.text, isNot(contains('@Bob'))); + }); + + test( + 'replaceMentionsWithId should not change message without mentions', + () { + final message = Message( + text: 'Hello, @Alice!', + ); + + final modifiedMessage = message.replaceMentionsWithId(); + + expect(modifiedMessage.text, equals('Hello, @Alice!')); + expect(modifiedMessage.text, isNot(contains('@user1'))); + }, + ); + + test('replaceMentionsWithId should handle message with only mention', () { + final user = User(id: 'user1', name: 'Alice'); + + final message = Message( + text: '@Alice', + mentionedUsers: [user], + ); + + final modifiedMessage = message.replaceMentionsWithId(); + + expect(modifiedMessage.text, contains('@user1')); + expect(modifiedMessage.text, isNot(contains('@Alice'))); + }); + }); } diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index 475bd5beb..aae7bb18b 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.8.0 + +- Updated minimum supported `SDK` version to Flutter 3.10/Dart 3.0 +- Updated `stream_chat` dependency to [`6.8.0`](https://pub.dev/packages/stream_chat/changelog). + ## 6.7.0 - Updated `stream_chat` dependency to [`6.7.0`](https://pub.dev/packages/stream_chat/changelog). diff --git a/packages/stream_chat_flutter_core/example/pubspec.yaml b/packages/stream_chat_flutter_core/example/pubspec.yaml index c92a4fc6f..4a45b71eb 100644 --- a/packages/stream_chat_flutter_core/example/pubspec.yaml +++ b/packages/stream_chat_flutter_core/example/pubspec.yaml @@ -4,8 +4,8 @@ publish_to: 'none' version: 1.0.0+1 environment: - sdk: '>=2.19.0 <4.0.0' - flutter: ">=3.7.0" + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" dependencies: cupertino_icons: ^1.0.3 diff --git a/packages/stream_chat_flutter_core/pubspec.yaml b/packages/stream_chat_flutter_core/pubspec.yaml index e192c6558..655421b53 100644 --- a/packages/stream_chat_flutter_core/pubspec.yaml +++ b/packages/stream_chat_flutter_core/pubspec.yaml @@ -1,29 +1,29 @@ name: stream_chat_flutter_core homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter. -version: 6.7.0 +version: 6.8.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues environment: - sdk: ">=2.19.0 <4.0.0" - flutter: ">=3.7.0" + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" dependencies: - collection: ^1.17.0 - connectivity_plus: ^4.0.1 + collection: ^1.17.1 + connectivity_plus: ^4.0.2 flutter: sdk: flutter - freezed_annotation: ^2.2.0 - meta: ^1.8.0 + freezed_annotation: ^2.4.1 + meta: ^1.9.1 rxdart: ^0.27.7 - stream_chat: ^6.7.0 + stream_chat: ^6.8.0 dev_dependencies: - build_runner: ^2.3.3 + build_runner: ^2.4.6 fake_async: ^1.3.1 flutter_test: sdk: flutter - freezed: ^2.4.0 - mocktail: ^0.3.0 + freezed: ^2.4.1 + mocktail: ^1.0.0 diff --git a/packages/stream_chat_localizations/CHANGELOG.md b/packages/stream_chat_localizations/CHANGELOG.md index c397739aa..98a08c7c2 100644 --- a/packages/stream_chat_localizations/CHANGELOG.md +++ b/packages/stream_chat_localizations/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.9.0 + +* Updated minimum supported `SDK` version to Flutter 3.10/Dart 3.0 +* Updated `stream_chat_flutter` dependency to [`6.9.0`](https://pub.dev/packages/stream_chat_flutter/changelog). + ## 5.8.0 * Updated `stream_chat_flutter` dependency to [`6.8.0`](https://pub.dev/packages/stream_chat_flutter/changelog). diff --git a/packages/stream_chat_localizations/example/lib/add_new_lang.dart b/packages/stream_chat_localizations/example/lib/add_new_lang.dart index bab8101b8..e1d1335ac 100644 --- a/packages/stream_chat_localizations/example/lib/add_new_lang.dart +++ b/packages/stream_chat_localizations/example/lib/add_new_lang.dart @@ -270,13 +270,15 @@ class NnStreamChatLocalizations extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'yesterday'; } else { - return 'on ${Jiffy(date).MMMd}'; + return 'on ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - 'Sent ${_getDay(date)} at ${Jiffy(time.toLocal()).format('HH:mm')}'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Sent ${_getDay(date)} at ${atTime.jm}'; + } @override String get todayLabel => 'Today'; diff --git a/packages/stream_chat_localizations/example/pubspec.yaml b/packages/stream_chat_localizations/example/pubspec.yaml index c23b28b1d..f3e11a290 100644 --- a/packages/stream_chat_localizations/example/pubspec.yaml +++ b/packages/stream_chat_localizations/example/pubspec.yaml @@ -5,8 +5,8 @@ publish_to: 'none' version: 1.0.0+1 environment: - sdk: '>=2.19.0 <4.0.0' - flutter: ">=3.7.0" + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" dependencies: cupertino_icons: ^1.0.3 diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ca.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ca.dart index 3c471e051..ed2d3d7ed 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ca.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ca.dart @@ -249,13 +249,15 @@ class StreamChatLocalizationsCa extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'ahir'; } else { - return 'el ${Jiffy(date).MMMd}'; + return 'el ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - '''Enviat el ${_getDay(date)} a les ${Jiffy(time.toLocal()).format('HH:mm')}'''; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Enviat el ${_getDay(date)} a les ${atTime.jm}'; + } @override String get todayLabel => 'Avui'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart index df2a7766b..fe95f0f7f 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart @@ -239,13 +239,15 @@ class StreamChatLocalizationsDe extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'Gestern'; } else { - return 'am ${Jiffy(date).MMMd}'; + return 'am ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - 'Gesendet ${_getDay(date)} am ${Jiffy(time.toLocal()).format('HH:mm')}'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Gesendet ${_getDay(date)} am ${atTime.jm}'; + } @override String get todayLabel => 'Heute'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart index c55bbf49d..a744ff2ae 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart @@ -246,13 +246,15 @@ class StreamChatLocalizationsEn extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'yesterday'; } else { - return 'on ${Jiffy(date).MMMd}'; + return 'on ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - 'Sent ${_getDay(date)} at ${Jiffy(time.toLocal()).format('HH:mm')}'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Sent ${_getDay(date)} at ${atTime.jm}'; + } @override String get todayLabel => 'Today'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart index 107e26166..2d75b1028 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart @@ -250,13 +250,15 @@ class StreamChatLocalizationsEs extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'ayer'; } else { - return 'el ${Jiffy(date).MMMd}'; + return 'el ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - '''Enviado el ${_getDay(date)} a las ${Jiffy(time.toLocal()).format('HH:mm')}'''; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Enviado el ${_getDay(date)} a las ${atTime.jm}'; + } @override String get todayLabel => 'Hoy'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart index 8b501184e..6a474b8a6 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart @@ -249,13 +249,15 @@ class StreamChatLocalizationsFr extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'hier'; } else { - return 'le ${Jiffy(date).MMMd}'; + return 'le ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - 'Envoyé ${_getDay(date)} à ${Jiffy(time.toLocal()).format('HH:mm')}'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Envoyé ${_getDay(date)} à ${atTime.jm}'; + } @override String get todayLabel => "Aujourd'hui"; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart index 4f27717a7..4e056ab22 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart @@ -243,13 +243,15 @@ class StreamChatLocalizationsHi extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'कल'; } else { - return '${Jiffy(date).MMMd} को'; + return '${Jiffy.parseFromDateTime(date).MMMd} को'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - '${_getDay(date)} ${Jiffy(time.toLocal()).format('HH:mm')} बजे भेजा गया'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return '${_getDay(date)} ${atTime.jm} बजे भेजा गया'; + } @override String get todayLabel => 'आज'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart index b407629d1..51a808972 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart @@ -252,13 +252,15 @@ Il file è troppo grande per essere caricato. Il limite è di $limitInMB MB.'''; } else if (date == yesterday) { return 'ieri'; } else { - return 'il ${Jiffy(date).MMMd}'; + return 'il ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - "Inviato ${_getDay(date)} alle ${Jiffy(time.toLocal()).format('HH:mm')}"; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Inviato ${_getDay(date)} alle ${atTime.jm}'; + } @override String get todayLabel => 'Oggi'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart index 26a5e8b3e..17285f6aa 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart @@ -236,13 +236,15 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return '昨日'; } else { - return '${Jiffy(date).MMMd}に'; + return '${Jiffy.parseFromDateTime(date).MMMd}に'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - '${_getDay(date)}の${Jiffy(time.toLocal()).format('HH:mm')}に送信しました '; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return '${_getDay(date)}の${atTime.jm}に送信しました '; + } @override String get todayLabel => '今日'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart index 7b907584e..acf1f79f9 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart @@ -235,13 +235,15 @@ class StreamChatLocalizationsKo extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return '어제'; } else { - return '${Jiffy(date).MMMd}에'; + return '${Jiffy.parseFromDateTime(date).MMMd}에'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - '${_getDay(date)} ${Jiffy(time.toLocal()).format('HH:mm')}에 보냈습니다'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return '${_getDay(date)} ${atTime.jm}에 보냈습니다'; + } @override String get todayLabel => '오늘'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_no.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_no.dart index 88350979e..8a6c4db31 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_no.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_no.dart @@ -242,13 +242,15 @@ class StreamChatLocalizationsNo extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'i går'; } else { - return 'på ${Jiffy(date).MMMd}'; + return 'på ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - 'Sent ${_getDay(date)} kl. ${Jiffy(time.toLocal()).format('HH:mm')}'; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Sent ${_getDay(date)} kl. ${atTime.jm}'; + } @override String get todayLabel => 'I dag'; diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart index 874eb1189..f359808b2 100644 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart +++ b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart @@ -244,13 +244,15 @@ class StreamChatLocalizationsPt extends GlobalStreamChatLocalizations { } else if (date == yesterday) { return 'Ontem'; } else { - return 'o ${Jiffy(date).MMMd}'; + return 'o ${Jiffy.parseFromDateTime(date).MMMd}'; } } @override - String sentAtText({required DateTime date, required DateTime time}) => - '''Enviado ${_getDay(date)} às ${Jiffy(time.toLocal()).format('HH:mm')}'''; + String sentAtText({required DateTime date, required DateTime time}) { + final atTime = Jiffy.parseFromDateTime(time.toLocal()); + return 'Enviado ${_getDay(date)} às ${atTime.jm}'; + } @override String get todayLabel => 'Hoje'; diff --git a/packages/stream_chat_localizations/pubspec.yaml b/packages/stream_chat_localizations/pubspec.yaml index a5c3abdcb..cc391977a 100644 --- a/packages/stream_chat_localizations/pubspec.yaml +++ b/packages/stream_chat_localizations/pubspec.yaml @@ -1,20 +1,20 @@ name: stream_chat_localizations description: The Official localizations for Stream Chat Flutter, a service for building chat applications -version: 5.8.0 +version: 5.9.0 homepage: 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 environment: - sdk: ">=2.19.0 <4.0.0" - flutter: ">=3.7.0" + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter - stream_chat_flutter: ^6.8.0 + stream_chat_flutter: ^6.9.0 dev_dependencies: flutter_test: diff --git a/packages/stream_chat_persistence/CHANGELOG.md b/packages/stream_chat_persistence/CHANGELOG.md index 8c58dffdb..394a54a1e 100644 --- a/packages/stream_chat_persistence/CHANGELOG.md +++ b/packages/stream_chat_persistence/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.8.0 + +- Updated minimum supported `SDK` version to Flutter 3.10/Dart 3.0 +- Updated `stream_chat` dependency to [`6.8.0`](https://pub.dev/packages/stream_chat/changelog). + ## 6.7.0 - [[#1683]](https://github.com/GetStream/stream-chat-flutter/issues/1683) Fixed SqliteException no such diff --git a/packages/stream_chat_persistence/example/pubspec.yaml b/packages/stream_chat_persistence/example/pubspec.yaml index f67961aa5..c384f1c8e 100644 --- a/packages/stream_chat_persistence/example/pubspec.yaml +++ b/packages/stream_chat_persistence/example/pubspec.yaml @@ -4,8 +4,8 @@ publish_to: 'none' version: 1.0.0+1 environment: - sdk: '>=2.19.0 <4.0.0' - flutter: ">=3.7.0" + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" dependencies: cupertino_icons: ^1.0.3 diff --git a/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart b/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart index 0ee57dd0c..4a266910e 100644 --- a/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart +++ b/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart @@ -41,16 +41,13 @@ class $ChannelsTable extends Channels .withConverter>($ChannelsTable.$converterconfig); static const VerificationMeta _frozenMeta = const VerificationMeta('frozen'); @override - late final GeneratedColumn frozen = - GeneratedColumn('frozen', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({ - SqlDialect.sqlite: 'CHECK ("frozen" IN (0, 1))', - SqlDialect.mysql: '', - SqlDialect.postgres: '', - }), - defaultValue: const Constant(false)); + late final GeneratedColumn frozen = GeneratedColumn( + 'frozen', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("frozen" IN (0, 1))'), + defaultValue: const Constant(false)); static const VerificationMeta _lastMessageAtMeta = const VerificationMeta('lastMessageAt'); @override @@ -726,28 +723,22 @@ class $MessagesTable extends Messages static const VerificationMeta _showInChannelMeta = const VerificationMeta('showInChannel'); @override - late final GeneratedColumn showInChannel = - GeneratedColumn('show_in_channel', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({ - SqlDialect.sqlite: 'CHECK ("show_in_channel" IN (0, 1))', - SqlDialect.mysql: '', - SqlDialect.postgres: '', - })); + late final GeneratedColumn showInChannel = GeneratedColumn( + 'show_in_channel', aliasedName, true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("show_in_channel" IN (0, 1))')); static const VerificationMeta _shadowedMeta = const VerificationMeta('shadowed'); @override - late final GeneratedColumn shadowed = - GeneratedColumn('shadowed', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({ - SqlDialect.sqlite: 'CHECK ("shadowed" IN (0, 1))', - SqlDialect.mysql: '', - SqlDialect.postgres: '', - }), - defaultValue: const Constant(false)); + late final GeneratedColumn shadowed = GeneratedColumn( + 'shadowed', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("shadowed" IN (0, 1))'), + defaultValue: const Constant(false)); static const VerificationMeta _commandMeta = const VerificationMeta('command'); @override @@ -797,16 +788,13 @@ class $MessagesTable extends Messages type: DriftSqlType.string, requiredDuringInsert: false); static const VerificationMeta _pinnedMeta = const VerificationMeta('pinned'); @override - late final GeneratedColumn pinned = - GeneratedColumn('pinned', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({ - SqlDialect.sqlite: 'CHECK ("pinned" IN (0, 1))', - SqlDialect.mysql: '', - SqlDialect.postgres: '', - }), - defaultValue: const Constant(false)); + late final GeneratedColumn pinned = GeneratedColumn( + 'pinned', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("pinned" IN (0, 1))'), + defaultValue: const Constant(false)); static const VerificationMeta _pinnedAtMeta = const VerificationMeta('pinnedAt'); @override @@ -2004,28 +1992,22 @@ class $PinnedMessagesTable extends PinnedMessages static const VerificationMeta _showInChannelMeta = const VerificationMeta('showInChannel'); @override - late final GeneratedColumn showInChannel = - GeneratedColumn('show_in_channel', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({ - SqlDialect.sqlite: 'CHECK ("show_in_channel" IN (0, 1))', - SqlDialect.mysql: '', - SqlDialect.postgres: '', - })); + late final GeneratedColumn showInChannel = GeneratedColumn( + 'show_in_channel', aliasedName, true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("show_in_channel" IN (0, 1))')); static const VerificationMeta _shadowedMeta = const VerificationMeta('shadowed'); @override - late final GeneratedColumn shadowed = - GeneratedColumn('shadowed', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({ - SqlDialect.sqlite: 'CHECK ("shadowed" IN (0, 1))', - SqlDialect.mysql: '', - SqlDialect.postgres: '', - }), - defaultValue: const Constant(false)); + late final GeneratedColumn shadowed = GeneratedColumn( + 'shadowed', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("shadowed" IN (0, 1))'), + defaultValue: const Constant(false)); static const VerificationMeta _commandMeta = const VerificationMeta('command'); @override @@ -2075,16 +2057,13 @@ class $PinnedMessagesTable extends PinnedMessages type: DriftSqlType.string, requiredDuringInsert: false); static const VerificationMeta _pinnedMeta = const VerificationMeta('pinned'); @override - late final GeneratedColumn pinned = - GeneratedColumn('pinned', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({ - SqlDialect.sqlite: 'CHECK ("pinned" IN (0, 1))', - SqlDialect.mysql: '', - SqlDialect.postgres: '', - }), - defaultValue: const Constant(false)); + late final GeneratedColumn pinned = GeneratedColumn( + 'pinned', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("pinned" IN (0, 1))'), + defaultValue: const Constant(false)); static const VerificationMeta _pinnedAtMeta = const VerificationMeta('pinnedAt'); @override @@ -3928,28 +3907,22 @@ class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> { type: DriftSqlType.dateTime, requiredDuringInsert: false); static const VerificationMeta _onlineMeta = const VerificationMeta('online'); @override - late final GeneratedColumn online = - GeneratedColumn('online', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({ - SqlDialect.sqlite: 'CHECK ("online" IN (0, 1))', - SqlDialect.mysql: '', - SqlDialect.postgres: '', - }), - defaultValue: const Constant(false)); + late final GeneratedColumn online = GeneratedColumn( + 'online', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("online" IN (0, 1))'), + defaultValue: const Constant(false)); static const VerificationMeta _bannedMeta = const VerificationMeta('banned'); @override - late final GeneratedColumn banned = - GeneratedColumn('banned', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({ - SqlDialect.sqlite: 'CHECK ("banned" IN (0, 1))', - SqlDialect.mysql: '', - SqlDialect.postgres: '', - }), - defaultValue: const Constant(false)); + late final GeneratedColumn banned = GeneratedColumn( + 'banned', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("banned" IN (0, 1))'), + defaultValue: const Constant(false)); static const VerificationMeta _extraDataMeta = const VerificationMeta('extraData'); @override @@ -4388,54 +4361,42 @@ class $MembersTable extends Members static const VerificationMeta _invitedMeta = const VerificationMeta('invited'); @override - late final GeneratedColumn invited = - GeneratedColumn('invited', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({ - SqlDialect.sqlite: 'CHECK ("invited" IN (0, 1))', - SqlDialect.mysql: '', - SqlDialect.postgres: '', - }), - defaultValue: const Constant(false)); + late final GeneratedColumn invited = GeneratedColumn( + 'invited', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("invited" IN (0, 1))'), + defaultValue: const Constant(false)); static const VerificationMeta _bannedMeta = const VerificationMeta('banned'); @override - late final GeneratedColumn banned = - GeneratedColumn('banned', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({ - SqlDialect.sqlite: 'CHECK ("banned" IN (0, 1))', - SqlDialect.mysql: '', - SqlDialect.postgres: '', - }), - defaultValue: const Constant(false)); + late final GeneratedColumn banned = GeneratedColumn( + 'banned', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("banned" IN (0, 1))'), + defaultValue: const Constant(false)); static const VerificationMeta _shadowBannedMeta = const VerificationMeta('shadowBanned'); @override - late final GeneratedColumn shadowBanned = - GeneratedColumn('shadow_banned', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({ - SqlDialect.sqlite: 'CHECK ("shadow_banned" IN (0, 1))', - SqlDialect.mysql: '', - SqlDialect.postgres: '', - }), - defaultValue: const Constant(false)); + late final GeneratedColumn shadowBanned = GeneratedColumn( + 'shadow_banned', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("shadow_banned" IN (0, 1))'), + defaultValue: const Constant(false)); static const VerificationMeta _isModeratorMeta = const VerificationMeta('isModerator'); @override - late final GeneratedColumn isModerator = - GeneratedColumn('is_moderator', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({ - SqlDialect.sqlite: 'CHECK ("is_moderator" IN (0, 1))', - SqlDialect.mysql: '', - SqlDialect.postgres: '', - }), - defaultValue: const Constant(false)); + late final GeneratedColumn isModerator = GeneratedColumn( + 'is_moderator', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_moderator" IN (0, 1))'), + defaultValue: const Constant(false)); static const VerificationMeta _createdAtMeta = const VerificationMeta('createdAt'); @override diff --git a/packages/stream_chat_persistence/pubspec.yaml b/packages/stream_chat_persistence/pubspec.yaml index c86b200d1..0656cea67 100644 --- a/packages/stream_chat_persistence/pubspec.yaml +++ b/packages/stream_chat_persistence/pubspec.yaml @@ -1,29 +1,29 @@ name: stream_chat_persistence homepage: https://github.com/GetStream/stream-chat-flutter description: Official Stream Chat Persistence library. Build your own chat experience using Dart and Flutter. -version: 6.7.0 +version: 6.8.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues environment: - sdk: ">=2.19.0 <4.0.0" - flutter: ">=3.7.0" + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" dependencies: - drift: ^2.8.0 + drift: ^2.11.0 flutter: sdk: flutter logging: ^1.2.0 - meta: ^1.8.0 - path: ^1.8.2 - path_provider: ^2.0.15 + meta: ^1.9.1 + path: ^1.8.3 + path_provider: ^2.1.0 sqlite3_flutter_libs: ^0.5.15 - stream_chat: ^6.7.0 + stream_chat: ^6.8.0 dev_dependencies: - build_runner: ^2.3.3 - drift_dev: ^2.8.3 + build_runner: ^2.4.6 + drift_dev: ^2.11.0 flutter_test: sdk: flutter - mocktail: ^0.3.0 + mocktail: ^1.0.0 \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index cf418d76f..8c61d28a6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_flutter_workspace environment: - sdk: '>=2.19.0 <4.0.0' + sdk: '>=3.0.0 <4.0.0' dev_dependencies: melos: ^3.1.0