Merge branch 'develop'

This commit is contained in:
Salvatore Giordano
2022-06-20 16:35:19 +02:00
50 changed files with 923 additions and 605 deletions
@@ -7,6 +7,7 @@ env:
on: on:
pull_request: pull_request:
types: [opened, synchronize, reopened, ready_for_review, converted_to_draft]
push: push:
branches: branches:
- master - master
+1 -1
View File
@@ -82,7 +82,7 @@ To run a script, use `melos run <script name>`.
# How can I contribute? # How can I contribute?
Are you ready to dive into code? It's pretty easy to get up and running with your first Stream contribution. If this is your first time sending a PR to Stream, please read the above section on [local setup](https://www.notion.so/Stream-s-Contribution-Guide-e18e1d57295f4fa8836a115d3fa3d5e7) before continuing. Are you ready to dive into code? It's pretty easy to get up and running with your first Stream contribution. If this is your first time sending a PR to Stream, please read the above section on [local setup](https://github.com/GetStream/stream-chat-flutter/blob/develop/CONTRIBUTING.md#local-setup) before continuing.
## Filing bugs 🐛 ## Filing bugs 🐛
@@ -24,26 +24,22 @@ Make sure to check the [StreamUserListController](./stream_user_list_controller.
```dart ```dart
class UserListPage extends StatefulWidget { class UserListPage extends StatefulWidget {
const UserListPage({ const UserListPage({Key? key}) : super(key: key);
Key? key,
required this.client,
}) : super(key: key);
final StreamChatClient client;
@override @override
State<UserListPage> createState() => _UserListPageState(); State<UserListPage> createState() => _UserListPageState();
} }
class _UserListPageState extends State<UserListPage> { class _UserListPageState extends State<UserListPage> {
late final _controller = StreamUserListController( late final StreamUserListController _userListController =
client: widget.client, StreamUserListController(
client: StreamChat.of(context).client,
limit: 25, limit: 25,
filter: Filter.and([ filter: Filter.and(
Filter.notEqual('id', StreamChat.of(context).currentUser!.id), [Filter.notEqual('id', StreamChat.of(context).currentUser!.id)],
]), ),
sort: [ sort: [
SortOption( const SortOption(
'name', 'name',
direction: 1, direction: 1,
), ),
@@ -51,29 +47,14 @@ class _UserListPageState extends State<UserListPage> {
); );
@override @override
void dispose() { Widget build(BuildContext context) {
_controller.dispose(); return RefreshIndicator(
super.dispose(); onRefresh: () => _userListController.refresh(),
child: StreamUserListView(
controller: _userListController,
),
);
} }
@override
Widget build(BuildContext context) => Scaffold(
body: RefreshIndicator(
onRefresh: _controller.refresh,
child: StreamChannelListView(
controller: _controller,
onChannelTap: (channel) => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => StreamChannel(
channel: channel,
child: const ChannelPage(),
),
),
),
),
),
);
} }
``` ```
@@ -89,3 +70,25 @@ StreamUsersListView(
}, },
), ),
``` ```
### Selecting Users
The `StreamUserListView` widget allows selecting users in a list. The `defaultWidget` returned can be customized to indicate that it has been selected.
```dart
Set<User> _selectedUsers = {};
StreamUserListView(
controller: _userListController,
itemBuilder: (context, users, index, defaultWidget) {
return defaultWidget.copyWith(
selected: _selectedUsers.contains(users[index]),
);
},
onUserTap: (user) {
setState(() {
_selectedUsers.add(user);
});
},
);
```
+99 -66
View File
@@ -1,3 +1,11 @@
## 4.3.0
🐞 Fixed
- [[#1135]](https://github.com/GetStream/stream-chat-flutter/issues/1135) Persistence was not
removing the hidden channels.
- Fix `x-stream-client` header generation.
## 4.2.0 ## 4.2.0
✅ Added ✅ Added
@@ -13,18 +21,20 @@
🔄 Changed 🔄 Changed
- Deprecated `PaginationParams.before` and `PaginationParams.after`. Use `PaginationParams.limit` instead. - Deprecated `PaginationParams.before` and `PaginationParams.after`. Use `PaginationParams.limit`
instead.
🐞 Fixed 🐞 Fixed
- [[#1147]](https://github.com/GetStream/stream-chat-flutter/issues/1147) `channel.unset` not updating the extra data - [[#1147]](https://github.com/GetStream/stream-chat-flutter/issues/1147) `channel.unset` not
stream. updating the extra data stream.
## 4.1.0 ## 4.1.0
✅ Added ✅ Added
- Added support for extra data in attachment file uploader. Thanks, [@rlee1990](https://github.com/rlee1990). - Added support for extra data in attachment file uploader.
Thanks, [@rlee1990](https://github.com/rlee1990).
🔄 Changed 🔄 Changed
@@ -49,14 +59,14 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
🐞 Fixed 🐞 Fixed
- Fixed reactions not working for threads in offline mode. - Fixed reactions not working for threads in offline mode.
- [[#1046]](https://github.com/GetStream/stream-chat-flutter/issues/1046) After `/mute` command on reload cannot access - [[#1046]](https://github.com/GetStream/stream-chat-flutter/issues/1046) After `/mute` command on
any channel. reload cannot access any channel.
- [[#1047]](https://github.com/GetStream/stream-chat-flutter/issues/1047) `own_capabilities` extraData missing after - [[#1047]](https://github.com/GetStream/stream-chat-flutter/issues/1047) `own_capabilities`
channel update. extraData missing after channel update.
- [[#1054]](https://github.com/GetStream/stream-chat-flutter/issues/1054) - [[#1054]](https://github.com/GetStream/stream-chat-flutter/issues/1054)
Fix `Unsupported operation: Cannot remove from an unmodifiable list`. Fix `Unsupported operation: Cannot remove from an unmodifiable list`.
- [[#1033]](https://github.com/GetStream/stream-chat-flutter/issues/1033) Hard delete from dashboard does not delete - [[#1033]](https://github.com/GetStream/stream-chat-flutter/issues/1033) Hard delete from dashboard
message from client. does not delete message from client.
- Send only `user_id` while reconnecting. - Send only `user_id` while reconnecting.
✅ Added ✅ Added
@@ -78,21 +88,22 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
🐞 Fixed 🐞 Fixed
- [[#1081]](https://github.com/GetStream/stream-chat-flutter/issues/1081) Fixed a bug with user reconnection. - [[#1081]](https://github.com/GetStream/stream-chat-flutter/issues/1081) Fixed a bug with user
reconnection.
## 3.6.0 ## 3.6.0
🐞 Fixed 🐞 Fixed
- Fixed reactions not working for threads in offline mode. - Fixed reactions not working for threads in offline mode.
- [[#1046]](https://github.com/GetStream/stream-chat-flutter/issues/1046) After `/mute` command on reload cannot access - [[#1046]](https://github.com/GetStream/stream-chat-flutter/issues/1046) After `/mute` command on
any channel. reload cannot access any channel.
- [[#1047]](https://github.com/GetStream/stream-chat-flutter/issues/1047) `own_capabilities` extraData missing after - [[#1047]](https://github.com/GetStream/stream-chat-flutter/issues/1047) `own_capabilities`
channel update. extraData missing after channel update.
- [[#1054]](https://github.com/GetStream/stream-chat-flutter/issues/1054) - [[#1054]](https://github.com/GetStream/stream-chat-flutter/issues/1054)
Fix `Unsupported operation: Cannot remove from an unmodifiable list`. Fix `Unsupported operation: Cannot remove from an unmodifiable list`.
- [[#1033]](https://github.com/GetStream/stream-chat-flutter/issues/1033) Hard delete from dashboard does not delete - [[#1033]](https://github.com/GetStream/stream-chat-flutter/issues/1033) Hard delete from dashboard
message from client. does not delete message from client.
- Send only `user_id` while reconnecting. - Send only `user_id` while reconnecting.
✅ Added ✅ Added
@@ -116,24 +127,26 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
🐞 Fixed 🐞 Fixed
- [[#890]](https://github.com/GetStream/stream-chat-flutter/pull/890) Fixed Reactions not updating on thread messages. - [[#890]](https://github.com/GetStream/stream-chat-flutter/pull/890) Fixed Reactions not updating
Thanks [bstolinski](https://github.com/bstolinski). on thread messages. Thanks [bstolinski](https://github.com/bstolinski).
- [[#897]](https://github.com/GetStream/stream-chat-flutter/issues/897) Fixed error type mis-match in `AuthInterceptor`. - [[#897]](https://github.com/GetStream/stream-chat-flutter/issues/897) Fixed error type mis-match
- [[#891]](https://github.com/GetStream/stream-chat-flutter/pull/891) Fixed reply counter for parent message not in `AuthInterceptor`.
updating correctly after deleting thread message. - [[#891]](https://github.com/GetStream/stream-chat-flutter/pull/891) Fixed reply counter for parent
message not updating correctly after deleting thread message.
- Fix `channelState.copyWith` with respect to pinnedMessages. - Fix `channelState.copyWith` with respect to pinnedMessages.
## 3.4.0 ## 3.4.0
🐞 Fixed 🐞 Fixed
- [[#857]](https://github.com/GetStream/stream-chat-flutter/issues/857) Channel now listens for member ban/unban and - [[#857]](https://github.com/GetStream/stream-chat-flutter/issues/857) Channel now listens for
updates the channel state with the latest data. member ban/unban and updates the channel state with the latest data.
- [[#748]](https://github.com/GetStream/stream-chat-flutter/issues/748) `Message.user` is now also included while saving - [[#748]](https://github.com/GetStream/stream-chat-flutter/issues/748) `Message.user` is now also
users in persistence. included while saving users in persistence.
- [[#871]](https://github.com/GetStream/stream-chat-flutter/issues/871) Fixed thread message deletion. - [[#871]](https://github.com/GetStream/stream-chat-flutter/issues/871) Fixed thread message
- [[#846]](https://github.com/GetStream/stream-chat-flutter/issues/846) Fixed `message.ownReactions` getting truncated deletion.
when receiving a reaction event. - [[#846]](https://github.com/GetStream/stream-chat-flutter/issues/846) Fixed `message.ownReactions`
getting truncated when receiving a reaction event.
- Add check for invalid image URLs - Add check for invalid image URLs
- Fix `channelState.pinnedMessagesStream` getting reset to `0` after a channel update. - Fix `channelState.pinnedMessagesStream` getting reset to `0` after a channel update.
- Fixed `unreadCount` after removing user from a channel. - Fixed `unreadCount` after removing user from a channel.
@@ -141,7 +154,8 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
🔄 Changed 🔄 Changed
- `client.location` is now deprecated in favor of the - `client.location` is now deprecated in favor of the
new [edge server](https://getstream.io/blog/chat-edge-infrastructure) and will be removed in v4.0.0. new [edge server](https://getstream.io/blog/chat-edge-infrastructure) and will be removed in
v4.0.0.
- `channel.banUser`, `channel.unbanUser` is now deprecated in favor of the new `channel.banMember` - `channel.banUser`, `channel.unbanUser` is now deprecated in favor of the new `channel.banMember`
and `channel.unbanMember`. These deprecated methods will be removed in v4.0.0. and `channel.unbanMember`. These deprecated methods will be removed in v4.0.0.
- Added `banExpires` property of type `DateTime` on the `Member`, `OwnUser`, and `User` models. - Added `banExpires` property of type `DateTime` on the `Member`, `OwnUser`, and `User` models.
@@ -155,8 +169,8 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
🐞 Fixed 🐞 Fixed
- [[#799]](https://github.com/GetStream/stream-chat-flutter/issues/799) Fixed `totalUnreadCount` is not updating when - [[#799]](https://github.com/GetStream/stream-chat-flutter/issues/799) Fixed `totalUnreadCount` is
app is resumed from background mode. not updating when app is resumed from background mode.
- Fix retry mechanism failing in some cases. - Fix retry mechanism failing in some cases.
## 3.3.0 ## 3.3.0
@@ -171,7 +185,8 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
- `closeConnection()` now uses `normalClosure` status when closing websocket. - `closeConnection()` now uses `normalClosure` status when closing websocket.
- Fixed local unread count indicator increasing for thread replies. - Fixed local unread count indicator increasing for thread replies.
- Fixed user presence indicator not updating correctly. - Fixed user presence indicator not updating correctly.
- `ChannelEvent.membersCount` defaults to 0 avoiding parsing errors due to missing `members_count` field. - `ChannelEvent.membersCount` defaults to 0 avoiding parsing errors due to missing `members_count`
field.
## 3.2.1 ## 3.2.1
@@ -184,7 +199,8 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
🐞 Fixed 🐞 Fixed
- `markAllRead()` now updates local channel states. - `markAllRead()` now updates local channel states.
- [[#744]](https://github.com/GetStream/stream-chat-flutter/issues/744) Fixed unread count not updating correctly - [[#744]](https://github.com/GetStream/stream-chat-flutter/issues/744) Fixed unread count not
updating correctly
## 3.1.1 ## 3.1.1
@@ -194,7 +210,8 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
🐞 Fixed 🐞 Fixed
- [[#710]](https://github.com/GetStream/stream-chat-flutter/issues/710) Fixed JWT requiring using `String` as id. - [[#710]](https://github.com/GetStream/stream-chat-flutter/issues/710) Fixed JWT requiring
using `String` as id.
- Fixed expired CDN attachment links not updating correctly. - Fixed expired CDN attachment links not updating correctly.
## 3.0.0 ## 3.0.0
@@ -214,16 +231,19 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
- Added `Filter.contains` and `Filter.empty` - Added `Filter.contains` and `Filter.empty`
- Added support for `next`, `previous` value pagination in `client.search` - Added support for `next`, `previous` value pagination in `client.search`
, [read more.](https://getstream.io/chat/docs/other-rest/search/#pagination) , [read more.](https://getstream.io/chat/docs/other-rest/search/#pagination)
- `Attachment` class now has a `fileSize` and `mimeType` property. Setting a `file` will also set the `file_size` - `Attachment` class now has a `fileSize` and `mimeType` property. Setting a `file` will also set
the `file_size`
, `mime_type` key on `extraData`, so `attachment.fileSize`, `attachment.mimetype` , `mime_type` key on `extraData`, so `attachment.fileSize`, `attachment.mimetype`
and `attachment.extraData['file_size']` and `attachment.extraData['file_size']`
, `attachment.extraData['mime_type]` is same respectively. , `attachment.extraData['mime_type]` is same respectively.
🐞 Fixed 🐞 Fixed
- [[#659]](https://github.com/GetStream/stream-chat-flutter/issues/659) Fixed unread count not updating correctly. - [[#659]](https://github.com/GetStream/stream-chat-flutter/issues/659) Fixed unread count not
updating correctly.
- Fix `Filter.empty()` json encoding. - Fix `Filter.empty()` json encoding.
- [[#700]](https://github.com/GetStream/stream-chat-flutter/issues/700) Connecting user without providing `name` - [[#700]](https://github.com/GetStream/stream-chat-flutter/issues/700) Connecting user without
providing `name`
uses `id` instead for setting `user.name`. uses `id` instead for setting `user.name`.
## 2.2.1 ## 2.2.1
@@ -241,14 +261,14 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
✅ Added ✅ Added
- `User` and `OwnUser` classes now have an `image` property. Setting an image will also set the 'image' key - `User` and `OwnUser` classes now have an `image` property. Setting an image will also set the '
on `extraData`, so `user.image` and `user.extraData['image']` is the same. image' key on `extraData`, so `user.image` and `user.extraData['image']` is the same.
- `User` and `OwnUser` classes now have a `name` property. Setting a name will also set the 'name' key on `extraData`, - `User` and `OwnUser` classes now have a `name` property. Setting a name will also set the 'name'
so `user.name` and `user.extraData['name']` is the same. key on `extraData`, so `user.name` and `user.extraData['name']` is the same.
- `Channel` class now has extra `image` getter and setter. As well as an `updateImage` to do a partial update after a - `Channel` class now has extra `image` getter and setter. As well as an `updateImage` to do a
channel has been initialized. partial update after a channel has been initialized.
- `Channel` class now has extra `name` getter and setter. As well as an `updateName` to do a partial update after a - `Channel` class now has extra `name` getter and setter. As well as an `updateName` to do a partial
channel has been initialized. update after a channel has been initialized.
- Added slow mode which allows a cooldown period after a user sends a message. - Added slow mode which allows a cooldown period after a user sends a message.
## 2.1.1 ## 2.1.1
@@ -261,7 +281,8 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
🛑️ Removed 🛑️ Removed
- The `MessageTranslation` class has been removed. Use the new `i18n` field in the `Message` class instead. - The `MessageTranslation` class has been removed. Use the new `i18n` field in the `Message` class
instead.
✅ Added ✅ Added
@@ -275,7 +296,8 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
🐞 Fixed 🐞 Fixed
- [#563](https://github.com/GetStream/stream-chat-flutter/issues/563): `Channel.stopWatching()` not working - [#563](https://github.com/GetStream/stream-chat-flutter/issues/563): `Channel.stopWatching()` not
working
- [#575](https://github.com/GetStream/stream-chat-flutter/issues/575): Wrong `OwnUser.*` - [#575](https://github.com/GetStream/stream-chat-flutter/issues/575): Wrong `OwnUser.*`
## 2.0.0 ## 2.0.0
@@ -283,7 +305,8 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
🛑️ Breaking Changes from `1.5.3` 🛑️ Breaking Changes from `1.5.3`
- migrate this package to null safety - migrate this package to null safety
- `ConnectUserWithProvider` now requires `tokenProvider` as a required param. (Removed from the constructor) - `ConnectUserWithProvider` now requires `tokenProvider` as a required param. (Removed from the
constructor)
- `client.disconnect()` is now divided into two different functions - `client.disconnect()` is now divided into two different functions
- `client.closeConnection()` -> for closing user websocket connection. - `client.closeConnection()` -> for closing user websocket connection.
- `client.disconnectUser()` -> for disconnecting user and resetting client state. - `client.disconnectUser()` -> for disconnecting user and resetting client state.
@@ -303,15 +326,16 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
🐞 Fixed 🐞 Fixed
- [#369](https://github.com/GetStream/stream-chat-flutter/issues/369): Client does not return without internet - [#369](https://github.com/GetStream/stream-chat-flutter/issues/369): Client does not return
connection without internet connection
- several minor fixes - several minor fixes
- performance improvements - performance improvements
✅ Added ✅ Added
- New `Location` enum is introduced for easily changing the client location/baseUrl. - New `Location` enum is introduced for easily changing the client location/baseUrl.
- New `client.openConnection()` and `client.closeConnection()` is introduced to connect/disconnect user ws connection. - New `client.openConnection()` and `client.closeConnection()` is introduced to connect/disconnect
user ws connection.
- New `client.partialUpdateMessage` and `channel.partialUpdateMessage` methods - New `client.partialUpdateMessage` and `channel.partialUpdateMessage` methods
- `connectWebSocket` parameter in connect user calls to use the client in "connection-less" mode. - `connectWebSocket` parameter in connect user calls to use the client in "connection-less" mode.
@@ -329,7 +353,8 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
🛑️ Breaking Changes from `2.0.0-nullsafety.6` 🛑️ Breaking Changes from `2.0.0-nullsafety.6`
- `ConnectUserWithProvider` now requires `tokenProvider` as a required param. (Removed from the constructor) - `ConnectUserWithProvider` now requires `tokenProvider` as a required param. (Removed from the
constructor)
- `client.disconnect()` is now divided into two different functions - `client.disconnect()` is now divided into two different functions
- `client.closeConnection()` -> for closing user websocket connection. - `client.closeConnection()` -> for closing user websocket connection.
- `client.disconnectUser()` -> for disconnecting user and resetting client state. - `client.disconnectUser()` -> for disconnecting user and resetting client state.
@@ -348,7 +373,8 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
✅ Added ✅ Added
- New `Location` enum is introduced for easily changing the client location/baseUrl. - New `Location` enum is introduced for easily changing the client location/baseUrl.
- New `client.openConnection()` and `client.closeConnection()` is introduced to connect/disconnect user ws connection. - New `client.openConnection()` and `client.closeConnection()` is introduced to connect/disconnect
user ws connection.
🔄 Changed 🔄 Changed
@@ -412,14 +438,16 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
- Save pinned messages in offline storage - Save pinned messages in offline storage
- Minor fixes - Minor fixes
- `StreamClient.QueryChannels` now returns a Stream and fetches the channels from storage before calling the api - `StreamClient.QueryChannels` now returns a Stream and fetches the channels from storage before
- Added `StreamClient.QueryChannelsOnline` and `StreamClient.QueryChannelsOffline` to fetch channels only from online or calling the api
offline - Added `StreamClient.QueryChannelsOnline` and `StreamClient.QueryChannelsOffline` to fetch channels
only from online or offline
## 1.2.0-beta ## 1.2.0-beta
- 🛑 **BREAKING** Changed signature of `StreamClient.search` method - 🛑 **BREAKING** Changed signature of `StreamClient.search` method
- Added `pinMessage` feature [docs here](https://getstream.io/chat/docs/flutter-dart/pinned_messages/?language=dart) - Added `pinMessage`
feature [docs here](https://getstream.io/chat/docs/flutter-dart/pinned_messages/?language=dart)
- Fixed minor bugs - Fixed minor bugs
## 1.1.0-beta ## 1.1.0-beta
@@ -436,7 +464,8 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
## 1.0.2-beta ## 1.0.2-beta
- Deprecated `setUser`, `setGuestUser`, `setUserWithProvider` in favor of `connectUser`, `connectGuestUser` - Deprecated `setUser`, `setGuestUser`, `setUserWithProvider` in favor of `connectUser`
, `connectGuestUser`
, `connectUserWithProvider` , `connectUserWithProvider`
- Optimised reaction updates - i.e., Update first call Api later. - Optimised reaction updates - i.e., Update first call Api later.
@@ -449,9 +478,11 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
- 🛑 **BREAKING** Renamed `Client` to less generic `StreamChatClient` - 🛑 **BREAKING** Renamed `Client` to less generic `StreamChatClient`
- 🛑 **BREAKING** Segregated the persistence layer into separate - 🛑 **BREAKING** Segregated the persistence layer into separate
package [stream_chat_persistence](https://pub.dev/packages/stream_chat_persistence) package [stream_chat_persistence](https://pub.dev/packages/stream_chat_persistence)
- 🛑 **BREAKING** Moved `Client.backgroundKeepAlive` to [core package](https://pub.dev/packages/stream_chat_core) - 🛑 **BREAKING** Moved `Client.backgroundKeepAlive`
- 🛑 **BREAKING** Moved `Client.showLocalNotification` to [core package](https://pub.dev/packages/stream_chat_core) and to [core package](https://pub.dev/packages/stream_chat_core)
renamed it to `StreamChatCore.onBackgroundEventReceived` - 🛑 **BREAKING** Moved `Client.showLocalNotification`
to [core package](https://pub.dev/packages/stream_chat_core) and renamed it
to `StreamChatCore.onBackgroundEventReceived`
- Removed `flutter` dependency. This is now a pure Dart package 🥳 - Removed `flutter` dependency. This is now a pure Dart package 🥳
- Minor improvements and bugfixes - Minor improvements and bugfixes
@@ -475,7 +506,8 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
## 0.2.23+2 ## 0.2.23+2
- Do not throw an error when calling queryChannels without an active connection if the offline storage is enabled - Do not throw an error when calling queryChannels without an active connection if the offline
storage is enabled
## 0.2.23+1 ## 0.2.23+1
@@ -506,8 +538,8 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
## 0.2.20 ## 0.2.20
- Return offline data only if the backend is unreachable. This avoids the glitch of the ChannelListView because we - Return offline data only if the backend is unreachable. This avoids the glitch of the
cannot sort by custom properties. ChannelListView because we cannot sort by custom properties.
## 0.2.19 ## 0.2.19
@@ -561,7 +593,8 @@ the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migra
## 0.2.12 ## 0.2.12
- Do not save channels in memory if not being watched. This was leading to some bugs in some specific use-cases. - Do not save channels in memory if not being watched. This was leading to some bugs in some
specific use-cases.
## 0.2.11 ## 0.2.11
+14 -11
View File
@@ -98,7 +98,9 @@ class StreamChatClient {
tokenManager: _tokenManager, tokenManager: _tokenManager,
handler: handleEvent, handler: handleEvent,
logger: detachedLogger('🔌'), logger: detachedLogger('🔌'),
queryParameters: {'X-Stream-Client': defaultUserAgent}, queryParameters: {
'X-Stream-Client': '$defaultUserAgent-$packageVersion',
},
); );
_retryPolicy = retryPolicy ?? _retryPolicy = retryPolicy ??
@@ -124,13 +126,15 @@ class StreamChatClient {
} }
/// Default user agent for all requests /// Default user agent for all requests
static String defaultUserAgent = 'stream-chat-dart-client-' static String defaultUserAgent =
'${CurrentPlatform.name}-' 'stream-chat-dart-client-${CurrentPlatform.name}';
'${PACKAGE_VERSION.split('+')[0]}';
/// Additional headers for all requests /// Additional headers for all requests
static Map<String, Object?> additionalHeaders = {}; static Map<String, Object?> additionalHeaders = {};
/// The current package version
static const packageVersion = PACKAGE_VERSION;
ChatPersistenceClient? _originalChatPersistenceClient; ChatPersistenceClient? _originalChatPersistenceClient;
/// Chat persistence client /// Chat persistence client
@@ -1489,13 +1493,12 @@ class ClientState {
} }
void _listenChannelHidden() { void _listenChannelHidden() {
_subscriptions.add(_client.on(EventType.channelHidden).listen((event) { _subscriptions
final cid = event.cid; .add(_client.on(EventType.channelHidden).listen((event) async {
final eventChannel = event.channel!;
if (cid != null) { await _client.chatPersistenceClient?.deleteChannels([eventChannel.cid]);
_client.chatPersistenceClient?.deleteChannels([cid]); channels[eventChannel.cid]?.dispose();
} channels = channels..remove(eventChannel.cid);
channels = channels..removeWhere((cid, ch) => cid == event.cid);
})); }));
} }
@@ -8,7 +8,7 @@ import 'package:stream_chat/src/core/http/token_manager.dart';
/// Authentication interceptor that refreshes the token if /// Authentication interceptor that refreshes the token if
/// an auth error is received /// an auth error is received
class AuthInterceptor extends Interceptor { class AuthInterceptor extends QueuedInterceptor {
/// Initialize a new auth interceptor /// Initialize a new auth interceptor
AuthInterceptor(this._client, this._tokenManager); AuthInterceptor(this._client, this._tokenManager);
@@ -57,35 +57,10 @@ class AuthInterceptor extends Interceptor {
final error = ErrorResponse.fromJson(data); final error = ErrorResponse.fromJson(data);
if (error.code == ChatErrorCode.tokenExpired.code) { if (error.code == ChatErrorCode.tokenExpired.code) {
if (_tokenManager.isStatic) return handler.next(err); if (_tokenManager.isStatic) return handler.next(err);
_client.lock();
await _tokenManager.loadToken(refresh: true); await _tokenManager.loadToken(refresh: true);
_client.unlock();
try { try {
final options = err.requestOptions; final options = err.requestOptions;
final response = await _client.request( final response = await _client.fetch(options);
options.path,
cancelToken: options.cancelToken,
data: options.data,
onReceiveProgress: options.onReceiveProgress,
onSendProgress: options.onSendProgress,
queryParameters: options.queryParameters,
options: Options(
method: options.method,
sendTimeout: options.sendTimeout,
receiveTimeout: options.receiveTimeout,
extra: options.extra,
headers: options.headers,
responseType: options.responseType,
contentType: options.contentType,
validateStatus: options.validateStatus,
receiveDataWhenStatusError: options.receiveDataWhenStatusError,
followRedirects: options.followRedirects,
maxRedirects: options.maxRedirects,
requestEncoder: options.requestEncoder,
responseDecoder: options.responseDecoder,
listFormat: options.listFormat,
),
);
return handler.resolve(response); return handler.resolve(response);
} on DioError catch (error) { } on DioError catch (error) {
return handler.next(error); return handler.next(error);
@@ -76,20 +76,6 @@ class StreamHttpClient {
@visibleForTesting @visibleForTesting
final Dio httpClient; final Dio httpClient;
/// Lock the current [StreamHttpClient] instance.
///
/// [StreamHttpClient] will enqueue the incoming request tasks instead
/// send them directly when [interceptor.requestOptions] is locked.
void lock() => httpClient.lock();
/// Unlock the current [StreamHttpClient] instance.
///
/// [StreamHttpClient] instance dequeue the request task。
void unlock() => httpClient.unlock();
/// Clear the current [StreamHttpClient] instance waiting queue.
void clear() => httpClient.clear();
/// Shuts down the [StreamHttpClient]. /// Shuts down the [StreamHttpClient].
/// ///
/// If [force] is `false` the [StreamHttpClient] will be kept alive /// If [force] is `false` the [StreamHttpClient] will be kept alive
@@ -280,4 +266,17 @@ class StreamHttpClient {
throw _parseError(error); throw _parseError(error);
} }
} }
/// Handy method to make http requests from [RequestOptions]
/// with error parsing.
Future<Response<T>> fetch<T>(
RequestOptions requestOptions,
) async {
try {
final response = await httpClient.fetch<T>(requestOptions);
return response;
} on DioError catch (error) {
throw _parseError(error);
}
}
} }
@@ -82,7 +82,7 @@ class User extends Equatable {
/// {@macro name} /// {@macro name}
@JsonKey(ignore: true) @JsonKey(ignore: true)
String get name { String get name {
if (extraData.containsKey('name')) { if (extraData.containsKey('name') && extraData['name'] != null) {
final name = extraData['name']! as String; final name = extraData['name']! as String;
if (name.isNotEmpty) return name; if (name.isNotEmpty) return name;
} }
+1 -1
View File
@@ -3,4 +3,4 @@ import 'package:stream_chat/src/client/client.dart';
/// Current package version /// Current package version
/// Used in [StreamChatClient] to build the `x-stream-client` header /// Used in [StreamChatClient] to build the `x-stream-client` header
// ignore: constant_identifier_names // ignore: constant_identifier_names
const PACKAGE_VERSION = '4.2.0'; const PACKAGE_VERSION = '4.3.0';
+1 -1
View File
@@ -1,7 +1,7 @@
name: stream_chat name: stream_chat
homepage: https://getstream.io/ homepage: https://getstream.io/
description: The official Dart client for Stream Chat, a service for building chat applications. description: The official Dart client for Stream Chat, a service for building chat applications.
version: 4.2.0 version: 4.3.0
repository: https://github.com/GetStream/stream-chat-flutter repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
@@ -2513,7 +2513,7 @@ void main() {
}); });
test( test(
'setting the `currentUser` should also compute and update the unreadCounts', '''setting the `currentUser` should also compute and update the unreadCounts''',
() { () {
final state = client.state; final state = client.state;
final initialUser = OwnUser.fromUser(user); final initialUser = OwnUser.fromUser(user);
@@ -93,23 +93,11 @@ void main() {
when(() => tokenManager.isStatic).thenReturn(false); when(() => tokenManager.isStatic).thenReturn(false);
when(() => client.lock()).thenReturn(() {});
final token = Token.development('test-user-id'); final token = Token.development('test-user-id');
when(() => tokenManager.loadToken(refresh: true)) when(() => tokenManager.loadToken(refresh: true))
.thenAnswer((_) async => token); .thenAnswer((_) async => token);
when(() => client.unlock()).thenReturn(() {}); when(() => client.fetch(options)).thenAnswer((_) async => Response(
when(() => client.request(
path,
data: options.data,
onReceiveProgress: options.onReceiveProgress,
onSendProgress: options.onSendProgress,
queryParameters: options.queryParameters,
cancelToken: options.cancelToken,
options: any(named: 'options'),
)).thenAnswer((_) async => Response(
requestOptions: options, requestOptions: options,
statusCode: 200, statusCode: 200,
)); ));
@@ -127,21 +115,10 @@ void main() {
verify(() => tokenManager.isStatic).called(1); verify(() => tokenManager.isStatic).called(1);
verify(() => client.lock()).called(1);
verify(() => tokenManager.loadToken(refresh: true)).called(1); verify(() => tokenManager.loadToken(refresh: true)).called(1);
verifyNoMoreInteractions(tokenManager); verifyNoMoreInteractions(tokenManager);
verify(() => client.unlock()).called(1); verify(() => client.fetch(options)).called(1);
verify(() => client.request(
path,
data: options.data,
onReceiveProgress: options.onReceiveProgress,
onSendProgress: options.onSendProgress,
queryParameters: options.queryParameters,
cancelToken: options.cancelToken,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(client); verifyNoMoreInteractions(client);
}); });
@@ -163,23 +140,11 @@ void main() {
when(() => tokenManager.isStatic).thenReturn(false); when(() => tokenManager.isStatic).thenReturn(false);
when(() => client.lock()).thenReturn(() {});
final token = Token.development('test-user-id'); final token = Token.development('test-user-id');
when(() => tokenManager.loadToken(refresh: true)) when(() => tokenManager.loadToken(refresh: true))
.thenAnswer((_) async => token); .thenAnswer((_) async => token);
when(() => client.unlock()).thenReturn(() {}); when(() => client.fetch(options)).thenThrow(err);
when(() => client.request(
path,
data: options.data,
onReceiveProgress: options.onReceiveProgress,
onSendProgress: options.onSendProgress,
queryParameters: options.queryParameters,
cancelToken: options.cancelToken,
options: any(named: 'options'),
)).thenThrow(err);
authInterceptor.onError(err, handler); authInterceptor.onError(err, handler);
@@ -193,21 +158,10 @@ void main() {
verify(() => tokenManager.isStatic).called(1); verify(() => tokenManager.isStatic).called(1);
verify(() => client.lock()).called(1);
verify(() => tokenManager.loadToken(refresh: true)).called(1); verify(() => tokenManager.loadToken(refresh: true)).called(1);
verifyNoMoreInteractions(tokenManager); verifyNoMoreInteractions(tokenManager);
verify(() => client.unlock()).called(1); verify(() => client.fetch(options)).called(1);
verify(() => client.request(
path,
data: options.data,
onReceiveProgress: options.onReceiveProgress,
onSendProgress: options.onSendProgress,
queryParameters: options.queryParameters,
cancelToken: options.cancelToken,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(client); verifyNoMoreInteractions(client);
}, },
); );
+159 -81
View File
@@ -1,45 +1,77 @@
## 4.3.0
- Updated `photo_view` dependency to [`0.14.0`](https://pub.dev/packages/photo_view/changelog).
🐞 Fixed
- [[#1180]](https://github.com/GetStream/stream-chat-flutter/issues/1180) Fix file download.
- Fix commands resetting the `StreamMessageInputController.value`.
- [[#996]](https://github.com/GetStream/stream-chat-flutter/issues/996) Videos break bottom photo
carousal.
- Fix: URLs with path and/or query params are not enriched.
- [[#1194]](https://github.com/GetStream/stream-chat-flutter/issues/1194) Request permission to access gallery when opening the file picker.
✅ Added
- [[#1011]](https://github.com/GetStream/stream-chat-flutter/issues/1011) Animate the background
color of pinned messages.
- Added unread messages divider in `StreamMessageListView`.
- Added `StreamMessageListView.unreadMessagesSeparatorBuilder`.
- Now `StreamMessageListView` opens to the oldest unread message by default.
## 4.2.0 ## 4.2.0
🐞 Fixed 🐞 Fixed
- [[#1133]](https://github.com/GetStream/stream-chat-flutter/issues/1133) Visibility override flags not being passed to `StreamMessageActionsModal` - [[#1133]](https://github.com/GetStream/stream-chat-flutter/issues/1133) Visibility override flags
not being passed to `StreamMessageActionsModal`
## 4.1.0 ## 4.1.0
✅ Added ✅ Added
- [[#1119]](https://github.com/GetStream/stream-chat-flutter/issues/1119) Added an option to disable mentions overlay in `StreamMessageInput` - [[#1119]](https://github.com/GetStream/stream-chat-flutter/issues/1119) Added an option to disable
- Deprecated `disableEmojiSuggestionsOverlay` in favor of `enableEmojiSuggestionsOverlay` in `StreamMessageInput` mentions overlay in `StreamMessageInput`
- Deprecated `disableEmojiSuggestionsOverlay` in favor of `enableEmojiSuggestionsOverlay`
in `StreamMessageInput`
🐞 Fixed 🐞 Fixed
- Fixed attachment picker ui. - Fixed attachment picker ui.
- Fixed StreamChannelHeader and StreamThreadHeader subtitle alignment. - Fixed StreamChannelHeader and StreamThreadHeader subtitle alignment.
- Fixed message widget thread indicator in reverse mode. - Fixed message widget thread indicator in reverse mode.
- [[#1044]](https://github.com/GetStream/stream-chat-flutter/issues/1044): Refactor StreamMessageWidget bottom row to use Text.rich. - [[#1044]](https://github.com/GetStream/stream-chat-flutter/issues/1044): Refactor
StreamMessageWidget bottom row to use Text.rich.
🔄 Changed 🔄 Changed
- Removed `isOwner` condition from `ChannelBottomSheet` and `StreamChannelInfoBottomSheet` for delete option tile. - Removed `isOwner` condition from `ChannelBottomSheet` and `StreamChannelInfoBottomSheet` for
delete option tile.
## 4.0.1 ## 4.0.1
- Minor fixes - Minor fixes
- Updated `stream_chat_flutter_core` dependency to [`4.0.1`](https://pub.dev/packages/stream_chat_flutter_core/changelog). - Updated `stream_chat_flutter_core` dependency
to [`4.0.1`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
## 4.0.0 ## 4.0.0
For upgrading to V4, please refer to the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migration_guide_4_0/) For upgrading to V4, please refer to
the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migration_guide_4_0/)
✅ Added ✅ Added
- [[#1087]](https://github.com/GetStream/stream-chat-flutter/issues/1087): Handle limited access to camera on iOS. - [[#1087]](https://github.com/GetStream/stream-chat-flutter/issues/1087): Handle limited access to
- `centerTitle` and `elevation` properties to `ChannelHeader`, `ThreadHeader` and `ChannelListHeader`. camera on iOS.
- `centerTitle` and `elevation` properties to `ChannelHeader`, `ThreadHeader`
and `ChannelListHeader`.
🐞 Fixed 🐞 Fixed
- [[#1067]](https://github.com/GetStream/stream-chat-flutter/issues/1067): Fix name text overflow in reaction card. - [[#1067]](https://github.com/GetStream/stream-chat-flutter/issues/1067): Fix name text overflow in
- [[#842]](https://github.com/GetStream/stream-chat-flutter/issues/842): show date divider for first message. reaction card.
- [[#842]](https://github.com/GetStream/stream-chat-flutter/issues/842): show date divider for first
message.
- Loosen up url check for attachment download. - Loosen up url check for attachment download.
- Use `ogScrapeUrl` for LinkAttachments. - Use `ogScrapeUrl` for LinkAttachments.
@@ -48,16 +80,20 @@ For upgrading to V4, please refer to the [V4 Migration Guide](https://getstream.
✅ Added ✅ Added
- Added support to pass `autoCorrect` to `StreamMessageInput` for the text input field - Added support to pass `autoCorrect` to `StreamMessageInput` for the text input field
- Added support to control the visibility of the default emoji suggestions overlay in `StreamMessageInput` - Added support to control the visibility of the default emoji suggestions overlay
in `StreamMessageInput`
- Added support to build custom widget for scrollToBottom in `StreamMessageListView` - Added support to build custom widget for scrollToBottom in `StreamMessageListView`
🐞 Fixed 🐞 Fixed
- Minor fixes and improvements - Minor fixes and improvements
-[[#892]](https://github.com/GetStream/stream-chat-flutter/issues/892): Fix default `initialAlignment` in `MessageListView`. -[[#892]](https://github.com/GetStream/stream-chat-flutter/issues/892): Fix
- Fix `MessageInputTheme.inputBackgroundColor` color not being used in some widgets of `MessageInput` default `initialAlignment` in `MessageListView`.
- Fix `MessageInputTheme.inputBackgroundColor` color not being used in some widgets
of `MessageInput`
- Removed dependency on `visibility_detector` - Removed dependency on `visibility_detector`
- [[#1071]](https://github.com/GetStream/stream-chat-flutter/issues/1071): Fixed the way attachment actions were handled in full screen - [[#1071]](https://github.com/GetStream/stream-chat-flutter/issues/1071): Fixed the way attachment
actions were handled in full screen
## 4.0.0-beta.1 ## 4.0.0-beta.1
@@ -70,22 +106,27 @@ For upgrading to V4, please refer to the [V4 Migration Guide](https://getstream.
- Deprecated `ChannelAvatar` in favor of `StreamChannelAvatar`. - Deprecated `ChannelAvatar` in favor of `StreamChannelAvatar`.
- Deprecated `ChannelName` in favor of `StreamChannelName`. - Deprecated `ChannelName` in favor of `StreamChannelName`.
- Deprecated `MessageInput` in favor of `StreamMessageInput`. - Deprecated `MessageInput` in favor of `StreamMessageInput`.
- Separated `MessageInput` widget in smaller components. (For example `CountDownButton`, `StreamAttachmentPicker`...) - Separated `MessageInput` widget in smaller components. (For example `CountDownButton`
- Updated `stream_chat_flutter_core` dependency to [`4.0.0-beta.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog). , `StreamAttachmentPicker`...)
- Updated `stream_chat_flutter_core` dependency
to [`4.0.0-beta.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
- Added OpenGraph preview support for links in `StreamMessageInput`. - Added OpenGraph preview support for links in `StreamMessageInput`.
- Removed video compression. - Removed video compression.
## 3.6.1 ## 3.6.1
- Updated `stream_chat_flutter_core` dependency to [`3.6.1`](https://pub.dev/packages/stream_chat_flutter_core/changelog). - Updated `stream_chat_flutter_core` dependency
to [`3.6.1`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
## 3.6.0 ## 3.6.0
🐞 Fixed 🐞 Fixed
- Minor fixes and improvements - Minor fixes and improvements
-[[#892]](https://github.com/GetStream/stream-chat-flutter/issues/892): Fix default `initialAlignment` in `MessageListView`. -[[#892]](https://github.com/GetStream/stream-chat-flutter/issues/892): Fix
- Fix `MessageInputTheme.inputBackgroundColor` color not being used in some widgets of `MessageInput` default `initialAlignment` in `MessageListView`.
- Fix `MessageInputTheme.inputBackgroundColor` color not being used in some widgets
of `MessageInput`
- Removed dependency on `visibility_detector` - Removed dependency on `visibility_detector`
## 3.5.1 ## 3.5.1
@@ -98,32 +139,35 @@ For upgrading to V4, please refer to the [V4 Migration Guide](https://getstream.
🐞 Fixed 🐞 Fixed
- Mentions overlay now doesn't overflow when there is not enough height available - Mentions overlay now doesn't overflow when there is not enough height available
- Updated `stream_chat_flutter_core` dependency to [`3.5.1`](https://pub.dev/packages/stream_chat_flutter_core/changelog). - Updated `stream_chat_flutter_core` dependency
to [`3.5.1`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
✅ Added ✅ Added
- `onLinkTap` for `MessageWidget` can now be passed down to `UrlAttachment`. - `onLinkTap` for `MessageWidget` can now be passed down to `UrlAttachment`.
## 3.5.0 ## 3.5.0
🐞 Fixed 🐞 Fixed
- [[#888]](https://github.com/GetStream/stream-chat-flutter/issues/888) Fix `unban` command not working in `MessageInput`. - [[#888]](https://github.com/GetStream/stream-chat-flutter/issues/888) Fix `unban` command not
- [[#805]](https://github.com/GetStream/stream-chat-flutter/issues/805) Updated chewie dependency version to 1.3.0 working in `MessageInput`.
- [[#805]](https://github.com/GetStream/stream-chat-flutter/issues/805) Updated chewie dependency
version to 1.3.0
- Fix `showScrollToBottom` in `MessageListView` not respecting false value. - Fix `showScrollToBottom` in `MessageListView` not respecting false value.
- Fix default `Channel` route not opening from `ChannelListView` when `ChannelAvatar` is tapped - Fix default `Channel` route not opening from `ChannelListView` when `ChannelAvatar` is tapped
## 3.4.0 ## 3.4.0
- Updated `stream_chat_flutter_core` dependency to [`3.4.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog). - Updated `stream_chat_flutter_core` dependency
to [`3.4.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
🐞 Fixed 🐞 Fixed
- SVG rendering fixes. - SVG rendering fixes.
- Use file extension instead of mimeType for downloading files. - Use file extension instead of mimeType for downloading files.
- [[#860]](https://github.com/GetStream/stream-chat-flutter/issues/860) CastError while compressing Videos. - [[#860]](https://github.com/GetStream/stream-chat-flutter/issues/860) CastError while compressing
Videos.
✅ Added ✅ Added
@@ -136,22 +180,26 @@ For upgrading to V4, please refer to the [V4 Migration Guide](https://getstream.
## 3.3.2 ## 3.3.2
- Updated `stream_chat_flutter_core` dependency to [`3.3.1`](https://pub.dev/packages/stream_chat_flutter_core/changelog). - Updated `stream_chat_flutter_core` dependency
to [`3.3.1`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
## 3.3.1 ## 3.3.1
✅ Added ✅ Added
- `MessageListView` now allows more better control over spacing after messages using `spacingWidgetBuilder`. - `MessageListView` now allows more better control over spacing after messages
using `spacingWidgetBuilder`.
- `StreamChannel` can now fetch messages around a message ID with the `queryAroundMessage` call. - `StreamChannel` can now fetch messages around a message ID with the `queryAroundMessage` call.
- Added `MessageListView.keyboardDismissBehavior` property. - Added `MessageListView.keyboardDismissBehavior` property.
🐞 Fixed 🐞 Fixed
- [[#766]](https://github.com/GetStream/stream-chat-flutter/issues/766) `AttachmentActionsModal` now has customisation options for actions. - [[#766]](https://github.com/GetStream/stream-chat-flutter/issues/766) `AttachmentActionsModal` now
has customisation options for actions.
- Fixed `MessageWidget` null errors associated with `channel.memberCount`. - Fixed `MessageWidget` null errors associated with `channel.memberCount`.
- Fixed adding attachments on web. - Fixed adding attachments on web.
- [[#767]](https://github.com/GetStream/stream-chat-flutter/issues/767): Fix `MessageInput` focus behaviour when sending messages. - [[#767]](https://github.com/GetStream/stream-chat-flutter/issues/767): Fix `MessageInput` focus
behaviour when sending messages.
- Fixed user presence indicator not updating correctly. - Fixed user presence indicator not updating correctly.
- Do not use `withData: true` in `FilePicker` calls. - Do not use `withData: true` in `FilePicker` calls.
- Fixed read indicator not updating correctly in specific situations. - Fixed read indicator not updating correctly in specific situations.
@@ -159,29 +207,35 @@ For upgrading to V4, please refer to the [V4 Migration Guide](https://getstream.
## 3.2.0 ## 3.2.0
- Updated Dart SDK constraints to `>=2.14.0 <3.0.0`. - Updated Dart SDK constraints to `>=2.14.0 <3.0.0`.
- Updated `stream_chat_flutter_core` dependency to [`3.2.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog). - Updated `stream_chat_flutter_core` dependency
to [`3.2.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
🐞 Fixed 🐞 Fixed
- Fixed message highlight animation alignment in `MessageListView`. - Fixed message highlight animation alignment in `MessageListView`.
- [[#491]](https://github.com/GetStream/stream-chat-flutter/issues/491): Fix `MediaListView` showing media in wrong order. - [[#491]](https://github.com/GetStream/stream-chat-flutter/issues/491): Fix `MediaListView` showing
media in wrong order.
- Fixed `MessageListView` initialIndex not working in some cases. - Fixed `MessageListView` initialIndex not working in some cases.
- Improved `MessageListView` rendering in case of reordering. - Improved `MessageListView` rendering in case of reordering.
- Fix image thumbnail generation when using Stream CDN. - Fix image thumbnail generation when using Stream CDN.
✅ Added ✅ Added
- `MessageListViewThemeData` now accepts a `DecorationImage` as a background image for `MessageListView`. - `MessageListViewThemeData` now accepts a `DecorationImage` as a background image
for `MessageListView`.
## 3.1.1 ## 3.1.1
- Updated `stream_chat_flutter_core` dependency to [`3.1.1`](https://pub.dev/packages/stream_chat_flutter_core/changelog). - Updated `stream_chat_flutter_core` dependency
to [`3.1.1`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
- Updated `file_picker`, `image_gallery_saver`, and `video_thumbnail` to the latest versions. - Updated `file_picker`, `image_gallery_saver`, and `video_thumbnail` to the latest versions.
🐞 Fixed 🐞 Fixed
- [[#687]](https://github.com/GetStream/stream-chat-flutter/issues/687): Fix Users losing their place in the conversation after replying in threads. - [[#687]](https://github.com/GetStream/stream-chat-flutter/issues/687): Fix Users losing their
- Fixed floating date stream subscription causing "Bad state: stream has already been listened.” error. place in the conversation after replying in threads.
- Fixed floating date stream subscription causing "Bad state: stream has already been listened.”
error.
- Fixed `String` capitalize extension not working on empty strings. - Fixed `String` capitalize extension not working on empty strings.
✅ Added ✅ Added
@@ -189,17 +243,21 @@ For upgrading to V4, please refer to the [V4 Migration Guide](https://getstream.
- Added `MessageInput.customOverlays` property to add custom overlays to the message input. - Added `MessageInput.customOverlays` property to add custom overlays to the message input.
- Added `MessageInput.mentionAllAppUsers` property to mention all app users in the message input. - Added `MessageInput.mentionAllAppUsers` property to mention all app users in the message input.
- The `MessageInput` now supports local search for channels with less than 100 members. - The `MessageInput` now supports local search for channels with less than 100 members.
- Added `MessageListView.paginationLoadingIndicatorBuilder` to override the default loading indicator shown while paginating the message list. - Added `MessageListView.paginationLoadingIndicatorBuilder` to override the default loading
- Added new `linkBackgroundColor` in `MessageTheme` for setting background colors of link attachments. indicator shown while paginating the message list.
- Added new `linkBackgroundColor` in `MessageTheme` for setting background colors of link
attachments.
⚠️ Deprecated ⚠️ Deprecated
- `MessageInput.mentionsTileBuilder` is now deprecated in favor of `MessageInput.userMentionsTileBuilder`. - `MessageInput.mentionsTileBuilder` is now deprecated in favor
of `MessageInput.userMentionsTileBuilder`.
- `MentionTile` is now deprecated in favor of `UserMentionsTile`. - `MentionTile` is now deprecated in favor of `UserMentionsTile`.
## 3.0.0 ## 3.0.0
- Updated `stream_chat_flutter_core` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog). - Updated `stream_chat_flutter_core` dependency
to [`3.0.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
🛑️ Breaking Changes from `2.2.1` 🛑️ Breaking Changes from `2.2.1`
@@ -207,16 +265,19 @@ For upgrading to V4, please refer to the [V4 Migration Guide](https://getstream.
🐞 Fixed 🐞 Fixed
- [[#668]](https://github.com/GetStream/stream-chat-flutter/issues/668): Fix `MessageInput` rendering errors in case - [[#668]](https://github.com/GetStream/stream-chat-flutter/issues/668): Fix `MessageInput`
there are no actions available to show. rendering errors in case there are no actions available to show.
- [[#349]](https://github.com/GetStream/stream-chat-flutter/issues/349): Fix `MessageInput` attachment render overflow error. - [[#349]](https://github.com/GetStream/stream-chat-flutter/issues/349): Fix `MessageInput`
attachment render overflow error.
- `MessageInput` overlays now follow the `MessageInput` focus. - `MessageInput` overlays now follow the `MessageInput` focus.
- [[#674]](https://github.com/GetStream/stream-chat-flutter/issues/674): Check scrollController is attached before calling jump in MessageListView. - [[#674]](https://github.com/GetStream/stream-chat-flutter/issues/674): Check scrollController is
attached before calling jump in MessageListView.
- Fixed `MessageListView` header and footer when `reverse: false`. - Fixed `MessageListView` header and footer when `reverse: false`.
🔄 Changed 🔄 Changed
- Animation curves changed from default `Curves.linear` to `Curves.easeOut` and `Curves.easeIn` for attachment controls. - Animation curves changed from default `Curves.linear` to `Curves.easeOut` and `Curves.easeIn` for
attachment controls.
- Removed default padding in `DateDivider` in `MessageListView` - Removed default padding in `DateDivider` in `MessageListView`
✅ Added ✅ Added
@@ -273,17 +334,20 @@ For upgrading to V4, please refer to the [V4 Migration Guide](https://getstream.
✅ Added ✅ Added
- [#516](https://github.com/GetStream/stream-chat-flutter/issues/516): - [#516](https://github.com/GetStream/stream-chat-flutter/issues/516):
Added `StreamChatThemeData.placeholderUserImage` for building a widget when the `UserAvatar` image is loading Added `StreamChatThemeData.placeholderUserImage` for building a widget when the `UserAvatar` image
is loading
- Added a `backgroundColor` property to the following widgets: - Added a `backgroundColor` property to the following widgets:
- `ChannelHeader` - `ChannelHeader`
- `ChannelListHeader` - `ChannelListHeader`
- `GalleryHeader` - `GalleryHeader`
- `GalleryFooter` - `GalleryFooter`
- `ThreadHeader` - `ThreadHeader`
- Added `MessageInput.attachmentLimit` in order to limit the no. of attachments that can be sent with a single message. - Added `MessageInput.attachmentLimit` in order to limit the no. of attachments that can be sent
- Added `MessageInput.onAttachmentLimitExceed` callback which will be called when the `attachmentLimit` is exceeded. with a single message.
This will override the default error alert behaviour. - Added `MessageInput.onAttachmentLimitExceed` callback which will be called when
- Added `MessageInput.attachmentButtonBuilder` and `MessageInput.commandButtonBuilder` for more customizations. the `attachmentLimit` is exceeded. This will override the default error alert behaviour.
- Added `MessageInput.attachmentButtonBuilder` and `MessageInput.commandButtonBuilder` for more
customizations.
```dart ```dart
typedef ActionButtonBuilder = Widget Function( typedef ActionButtonBuilder = Widget Function(
@@ -299,8 +363,9 @@ You can call `.copyWith` to customize just a subset of properties.
🔄 Changed 🔄 Changed
Theming has been upgraded! Most theme classes now have `InheritedTheme` classes associated with them, and have been Theming has been upgraded! Most theme classes now have `InheritedTheme` classes associated with
upgraded with some goodies like `lerp` functions. Here's the full naming breakdown: them, and have been upgraded with some goodies like `lerp` functions. Here's the full naming
breakdown:
* `AvatarTheme` is now `AvatarThemeData` * `AvatarTheme` is now `AvatarThemeData`
* `ChannelHeaderTheme` is now `ChannelHeaderThemeData` * `ChannelHeaderTheme` is now `ChannelHeaderThemeData`
@@ -317,18 +382,20 @@ upgraded with some goodies like `lerp` functions. Here's the full naming breakdo
🐞 Fixed 🐞 Fixed
- Fixed `MessageInput` textField case where `input` is not enabled if the file picked from the camera is null. - Fixed `MessageInput` textField case where `input` is not enabled if the file picked from the
camera is null.
- Fixed date dividers position/alignment in non reversed `MessageListView`. - Fixed date dividers position/alignment in non reversed `MessageListView`.
- Fixed `MessageListView` not opening to the right initialMessage if `StreamChannel.initialMessageId` is set. - Fixed `MessageListView` not opening to the right initialMessage
- Fixed null check errors when accessing `message.text` in `MessageWidget` and `MessageListView`; this occurred when if `StreamChannel.initialMessageId` is set.
sending a message with no text. - Fixed null check errors when accessing `message.text` in `MessageWidget` and `MessageListView`;
this occurred when sending a message with no text.
## 2.1.2 ## 2.1.2
🐞 Fixed 🐞 Fixed
- [#590](https://github.com/GetStream/stream-chat-flutter/issues/590): livestream use case, no members when sending - [#590](https://github.com/GetStream/stream-chat-flutter/issues/590): livestream use case, no
message members when sending message
## 2.1.1 ## 2.1.1
@@ -346,7 +413,8 @@ upgraded with some goodies like `lerp` functions. Here's the full naming breakdo
🔄 Changed 🔄 Changed
- `StreamChat.of(context).user` is now deprecated in favor of `StreamChat.of(context).currentUser`. - `StreamChat.of(context).user` is now deprecated in favor of `StreamChat.of(context).currentUser`.
- `StreamChat.of(context).userStream` is now deprecated in favor of `StreamChat.of(context).currentUserStream`. - `StreamChat.of(context).userStream` is now deprecated in favor
of `StreamChat.of(context).currentUserStream`.
🐞 Fixed 🐞 Fixed
@@ -399,7 +467,8 @@ You can call `.copyWith` to customize just a subset of properties
- Added video compress options (frame and quality) to `MessageInput` - Added video compress options (frame and quality) to `MessageInput`
- TypingIndicator now has a property called `parentId` to show typing indicator specific to threads - TypingIndicator now has a property called `parentId` to show typing indicator specific to threads
- [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView header/footer - [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView
header/footer
- `MessageWidget` accepts a `userAvatarBuilder` - `MessageWidget` accepts a `userAvatarBuilder`
- Added pinMessage ui support - Added pinMessage ui support
- Added `MessageListView.threadSeparatorBuilder` property - Added `MessageListView.threadSeparatorBuilder` property
@@ -408,10 +477,12 @@ You can call `.copyWith` to customize just a subset of properties
🐞 Fixed 🐞 Fixed
- [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text box when editing - [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text
message box when editing message
- Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator use case - Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator
- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without a reload use case
- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without
a reload
- `MessageListView` not rendering if the user is not a member of the channel - `MessageListView` not rendering if the user is not a member of the channel
- Fix `MessageInput` overflow when there are no actions - Fix `MessageInput` overflow when there are no actions
- Minor fixes and improvements - Minor fixes and improvements
@@ -464,15 +535,18 @@ You can call `.copyWith` to customize just a subset of properties.
✅ Added ✅ Added
- TypingIndicator now has a property called `parentId` to show typing indicator specific to threads - TypingIndicator now has a property called `parentId` to show typing indicator specific to threads
- [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView header/footer - [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView
header/footer
- `MessageWidget` accepts a `userAvatarBuilder` - `MessageWidget` accepts a `userAvatarBuilder`
🐞 Fixed 🐞 Fixed
- [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text box when editing - [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text
message box when editing message
- Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator use case - Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator
- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without a reload use case
- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without
a reload
- `MessageListView` not rendering if the user is not a member of the channel - `MessageListView` not rendering if the user is not a member of the channel
## 2.0.0-nullsafety.7 ## 2.0.0-nullsafety.7
@@ -542,7 +616,8 @@ You can call `.copyWith` to customize just a subset of properties.
- Show error messages as system and keep them in the message input - Show error messages as system and keep them in the message input
- Remove notification badge logic - Remove notification badge logic
- Use shimmer while loading images - Use shimmer while loading images
- Polished `StreamChatTheme` adding more options and a new `MessageInputTheme` dedicated to `MessageInput` - Polished `StreamChatTheme` adding more options and a new `MessageInputTheme` dedicated
to `MessageInput`
- Add possibility to specify custom message actions using `MessageWidget.customActions` - Add possibility to specify custom message actions using `MessageWidget.customActions`
- Added `MessageListView.onAttachmentTap` callback - Added `MessageListView.onAttachmentTap` callback
- Fixed message newline issue - Fixed message newline issue
@@ -599,7 +674,8 @@ You can call `.copyWith` to customize just a subset of properties.
- Improved api documentation - Improved api documentation
- Updated `stream_chat` dependency to `^1.0.0-beta` - Updated `stream_chat` dependency to `^1.0.0-beta`
- Extracted sample app into dedicated [repo](https://github.com/GetStream/flutter-samples) - Extracted sample app into dedicated [repo](https://github.com/GetStream/flutter-samples)
- Reimplemented existing widgets using [stream_chat_flutter_core](https://pub.dev/packages/stream_chat_flutter_core) - Reimplemented existing widgets
using [stream_chat_flutter_core](https://pub.dev/packages/stream_chat_flutter_core)
## 0.2.21 ## 0.2.21
@@ -616,8 +692,8 @@ You can call `.copyWith` to customize just a subset of properties.
## 0.2.20+2 ## 0.2.20+2
- Added `shouldAddChannel` to ChannelsBloc in order to check if a channel has to be added to the list when a new message - Added `shouldAddChannel` to ChannelsBloc in order to check if a channel has to be added to the
arrives list when a new message arrives
## 0.2.20+1 ## 0.2.20+1
@@ -651,7 +727,8 @@ You can call `.copyWith` to customize just a subset of properties.
## 0.2.16 ## 0.2.16
- Do not wrap channel preview builder. Users will have to implement they're custom onTap/onLongPress implementation - Do not wrap channel preview builder. Users will have to implement they're custom onTap/onLongPress
implementation
- Make public autofocus field of the TextField of message_input - Make public autofocus field of the TextField of message_input
## 0.2.15 ## 0.2.15
@@ -836,10 +913,11 @@ You can call `.copyWith` to customize just a subset of properties.
## 0.2.1-alpha+1 ## 0.2.1-alpha+1
- Removed the additional `Navigator` in `StreamChat` widget. It was added to make the app have the `StreamChat` widget - Removed the additional `Navigator` in `StreamChat` widget. It was added to make the app have
as ancestor in every route. Now the recommended way to add `StreamChat` to your app is using the `builder` property of the `StreamChat` widget as ancestor in every route. Now the recommended way to add `StreamChat` to
your `MaterialApp` widget. Otherwise you can use it in the usual way, but you need to add a `StreamChat` widget to your app is using the `builder` property of your `MaterialApp` widget. Otherwise you can use it in
every route of your app. Read [this issue](https://github.com/GetStream/stream-chat-flutter/issues/47) for more the usual way, but you need to add a `StreamChat` widget to every route of your app.
Read [this issue](https://github.com/GetStream/stream-chat-flutter/issues/47) for more
information. information.
```dart ```dart
@@ -941,8 +1019,8 @@ Widget build(BuildContext context) {
- Add gesture (vertical drag down) to close the keyboard - Add gesture (vertical drag down) to close the keyboard
- Add keyboard type parameters (set it to TextInputType.text to show the submit button that will even close the - Add keyboard type parameters (set it to TextInputType.text to show the submit button that will
keyboard) even close the keyboard)
The property showVideoFullScreen was added mainly because of this issue brianegan/chewie#261 The property showVideoFullScreen was added mainly because of this issue brianegan/chewie#261
@@ -10,8 +10,8 @@ class Application : FlutterApplication(), PluginRegistrantCallback {
super.onCreate() super.onCreate()
} }
override fun registerWith(registry: PluginRegistry?) { override fun registerWith(registry: PluginRegistry) {
PathProviderPlugin.registerWith(registry?.registrarFor( PathProviderPlugin.registerWith(registry.registrarFor(
"io.flutter.plugins.pathprovider.PathProviderPlugin")) "io.flutter.plugins.pathprovider.PathProviderPlugin"))
} }
} }
@@ -59,5 +59,7 @@
<key>NSMicrophoneUsageDescription</key> <key>NSMicrophoneUsageDescription</key>
<string>Explain why your app uses the mic</string> <string>Explain why your app uses the mic</string>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
</dict> </dict>
</plist> </plist>
@@ -225,11 +225,12 @@ class _StreamGalleryFooterState extends State<StreamGalleryFooter> {
if (attachment.type == 'video') { if (attachment.type == 'video') {
media = InkWell( media = InkWell(
onTap: () => widget.mediaSelectedCallBack!(index), onTap: () => widget.mediaSelectedCallBack!(index),
child: FittedBox( child: AspectRatio(
fit: BoxFit.cover, aspectRatio: 1,
child: StreamVideoThumbnailImage( child: StreamVideoThumbnailImage(
video: (attachment.file?.path ?? video: (attachment.file?.path ??
attachment.assetUrl)!, attachment.assetUrl)!,
fit: BoxFit.cover,
), ),
), ),
); );
@@ -77,6 +77,10 @@ abstract class Translations {
/// contains a parent message /// contains a parent message
String threadSeparatorText(int replyCount); String threadSeparatorText(int replyCount);
/// The text for showing the unread messages count
/// in the [StreamMessageListView]
String unreadMessagesSeparatorText(int unreadCount);
/// The label for "connected" in [StreamConnectionStatusBuilder] /// The label for "connected" in [StreamConnectionStatusBuilder]
String get connectedLabel; String get connectedLabel;
@@ -711,4 +715,12 @@ Attachment limit exceeded: it's not possible to add more than $limit attachments
@override @override
String get linkDisabledError => 'Links are disabled'; String get linkDisabledError => 'Links are disabled';
@override
String unreadMessagesSeparatorText(int unreadCount) {
if (unreadCount == 1) {
return '1 unread message';
}
return '$unreadCount unread messages';
}
} }
@@ -66,7 +66,7 @@ typedef UserMentionTileBuilder = Widget Function(
/// Widget builder for action button. /// Widget builder for action button.
/// ///
/// [defaultActionButton] is the default [IconButton] configuration, /// [defaultActionButton] is the default [IconButton] configuration,
/// use [defaultActionButton.copyWith] to easily customize it. /// use .copyWith to easily customize it.
typedef ActionButtonBuilder = Widget Function( typedef ActionButtonBuilder = Widget Function(
BuildContext context, BuildContext context,
IconButton defaultActionButton, IconButton defaultActionButton,
@@ -200,6 +200,7 @@ class StreamMessageListView extends StatefulWidget {
this.onSystemMessageTap, this.onSystemMessageTap,
this.showFloatingDateDivider = true, this.showFloatingDateDivider = true,
this.threadSeparatorBuilder, this.threadSeparatorBuilder,
this.unreadMessagesSeparatorBuilder,
this.messageListController, this.messageListController,
this.reverse = true, this.reverse = true,
this.paginationLimit = 20, this.paginationLimit = 20,
@@ -337,6 +338,10 @@ class StreamMessageListView extends StatefulWidget {
/// Builder used to build the thread separator in case it's a thread view /// Builder used to build the thread separator in case it's a thread view
final WidgetBuilder? threadSeparatorBuilder; final WidgetBuilder? threadSeparatorBuilder;
/// Builder used to build the unread message separator
final Widget Function(BuildContext context, int unreadCount)?
unreadMessagesSeparatorBuilder;
/// A [MessageListController] allows pagination. /// A [MessageListController] allows pagination.
/// Use [ChannelListController.paginateData] pagination. /// Use [ChannelListController.paginateData] pagination.
final MessageListController? messageListController; final MessageListController? messageListController;
@@ -363,6 +368,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
StreamChannelState? streamChannel; StreamChannelState? streamChannel;
late StreamChatThemeData _streamTheme; late StreamChatThemeData _streamTheme;
late List<String> _userPermissions; late List<String> _userPermissions;
late int unreadCount;
int get _initialIndex { int get _initialIndex {
final initialScrollIndex = widget.initialScrollIndex; final initialScrollIndex = widget.initialScrollIndex;
@@ -381,6 +387,11 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
if (index != 0) return index + 1; if (index != 0) return index + 1;
return index; return index;
} }
if (unreadCount > 0) {
return unreadCount + 1;
}
return 0; return 0;
} }
@@ -596,9 +607,12 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
return const SizedBox(height: 8); return const SizedBox(height: 8);
} }
if (i == 1 || i == itemCount - 4) return const Offstage(); if (i == 1 || i == itemCount - 4) {
return const Offstage();
}
late final Message message, nextMessage; late final Message message, nextMessage;
late Widget separator;
if (widget.reverse) { if (widget.reverse) {
message = messages[i - 1]; message = messages[i - 1];
nextMessage = messages[i - 2]; nextMessage = messages[i - 2];
@@ -611,7 +625,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
nextMessage.createdAt.toLocal(), nextMessage.createdAt.toLocal(),
Units.DAY, Units.DAY,
)) { )) {
return _buildDateDivider(nextMessage); separator = _buildDateDivider(nextMessage);
} }
final timeDiff = final timeDiff =
Jiffy(nextMessage.createdAt.toLocal()).diff( Jiffy(nextMessage.createdAt.toLocal()).diff(
@@ -644,13 +658,52 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
} }
if (spacingRules.isNotEmpty) { if (spacingRules.isNotEmpty) {
return widget.spacingWidgetBuilder separator = widget.spacingWidgetBuilder
?.call(context, spacingRules) ?? ?.call(context, spacingRules) ??
const SizedBox(height: 8); const SizedBox(height: 8);
} }
return widget.spacingWidgetBuilder separator = widget.spacingWidgetBuilder
?.call(context, [SpacingType.defaultSpacing]) ?? ?.call(context, [SpacingType.defaultSpacing]) ??
const SizedBox(height: 2); const SizedBox(height: 2);
if (!isThread && unreadCount > 0 && unreadCount == i - 1) {
final unreadMessagesSeparator = widget
.unreadMessagesSeparatorBuilder
?.call(context, unreadCount);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
separator,
unreadMessagesSeparator ??
Padding(
padding:
const EdgeInsets.symmetric(vertical: 8),
child: DecoratedBox(
decoration: BoxDecoration(
gradient:
_streamTheme.colorTheme.bgGradient,
),
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(
context.translations
.unreadMessagesSeparatorText(
unreadCount,
),
textAlign: TextAlign.center,
style:
StreamChannelHeaderTheme.of(context)
.subtitleStyle,
),
),
),
),
],
);
}
return separator;
}, },
itemBuilder: (context, i) { itemBuilder: (context, i) {
if (i == itemCount - 1) { if (i == itemCount - 1) {
@@ -880,9 +933,13 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
} }
Future<void> scrollToBottomDefaultTapAction(int unreadCount) async { Future<void> scrollToBottomDefaultTapAction(int unreadCount) async {
this.unreadCount = unreadCount;
if (unreadCount > 0) { if (unreadCount > 0) {
streamChannel!.channel.markRead(); streamChannel!.channel.markRead();
} }
final index = unreadCount > 0 ? unreadCount + 1 : 0;
if (!_upToDate) { if (!_upToDate) {
_bottomPaginationActive = false; _bottomPaginationActive = false;
initialAlignment = 0; initialAlignment = 0;
@@ -890,11 +947,11 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
await streamChannel!.reloadChannel(); await streamChannel!.reloadChannel();
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollController!.jumpTo(index: 0); _scrollController!.jumpTo(index: index);
}); });
} else { } else {
_scrollController!.scrollTo( _scrollController!.scrollTo(
index: 0, index: index,
duration: const Duration(seconds: 1), duration: const Duration(seconds: 1),
curve: Curves.easeInOut, curve: Curves.easeInOut,
); );
@@ -1317,6 +1374,8 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
if (newStreamChannel != streamChannel) { if (newStreamChannel != streamChannel) {
streamChannel = newStreamChannel; streamChannel = newStreamChannel;
_messageNewListener?.cancel(); _messageNewListener?.cancel();
unreadCount = streamChannel?.channel.state?.unreadCount ?? 0;
initialIndex = _initialIndex; initialIndex = _initialIndex;
initialAlignment = _initialAlignment; initialAlignment = _initialAlignment;
@@ -1328,25 +1387,38 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
} }
_messageNewListener = _messageNewListener =
streamChannel!.channel.on(EventType.messageNew).listen((event) { streamChannel!.channel.on(EventType.messageNew).skip(1)
//skipping the first event because
//the StreamController is a BehaviorSubject
.listen((event) {
if (_upToDate) { if (_upToDate) {
_bottomPaginationActive = false; _bottomPaginationActive = false;
} }
if (event.message?.parentId == widget.parentMessage?.id && if (event.message?.parentId == widget.parentMessage?.id &&
event.message!.user!.id == event.message!.user!.id ==
streamChannel!.channel.client.state.currentUser!.id) { streamChannel!.channel.client.state.currentUser!.id) {
setState(() {
unreadCount = 0;
});
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollController?.scrollTo( _scrollController?.scrollTo(
index: 0, index: 0,
duration: const Duration(seconds: 1), duration: const Duration(seconds: 1),
); );
}); });
} else if (streamChannel?.channel.state?.unreadCount != 0) {
setState(() {
unreadCount = unreadCount + 1;
});
} }
}); });
if (_isThreadConversation) { if (_isThreadConversation) {
streamChannel!.getReplies(widget.parentMessage!.id); streamChannel!.getReplies(widget.parentMessage!.id);
} }
unreadCount = streamChannel?.channel.state?.unreadCount ?? 0;
} }
super.didChangeDependencies(); super.didChangeDependencies();
@@ -596,240 +596,252 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
final showReactions = _shouldShowReactions; final showReactions = _shouldShowReactions;
final onMessageTap = widget.onMessageTap;
return Material( return Material(
type: widget.message.pinned && widget.showPinHighlight type: MaterialType.transparency,
? MaterialType.card child: AnimatedContainer(
: MaterialType.transparency, duration: const Duration(seconds: 1),
color: widget.message.pinned && widget.showPinHighlight color: widget.message.pinned && widget.showPinHighlight
? _streamChatTheme.colorTheme.highlight ? _streamChatTheme.colorTheme.highlight
: null, : _streamChatTheme.colorTheme.barsBg.withOpacity(0),
child: Portal( child: Portal(
child: InkWell( child: InkWell(
onTap: () { onTap: onMessageTap == null
widget.onMessageTap!(widget.message); ? null
}, : () => onMessageTap(widget.message),
onLongPress: widget.message.isDeleted && !isFailedState onLongPress: widget.message.isDeleted && !isFailedState
? null ? null
: () => onLongPress(context), : () => onLongPress(context),
child: Padding( child: Padding(
padding: widget.padding ?? const EdgeInsets.all(8), padding: widget.padding ?? const EdgeInsets.all(8),
child: FractionallySizedBox( child: FractionallySizedBox(
alignment: alignment: widget.reverse
widget.reverse ? Alignment.centerRight : Alignment.centerLeft, ? Alignment.centerRight
widthFactor: 0.78, : Alignment.centerLeft,
child: Column( widthFactor: 0.78,
crossAxisAlignment: widget.reverse child: Column(
? CrossAxisAlignment.end crossAxisAlignment: widget.reverse
: CrossAxisAlignment.start, ? CrossAxisAlignment.end
mainAxisSize: MainAxisSize.min, : CrossAxisAlignment.start,
children: <Widget>[ mainAxisSize: MainAxisSize.min,
Stack( children: <Widget>[
clipBehavior: Clip.none, Stack(
alignment: widget.reverse clipBehavior: Clip.none,
? AlignmentDirectional.bottomEnd alignment: widget.reverse
: AlignmentDirectional.bottomStart, ? AlignmentDirectional.bottomEnd
children: [ : AlignmentDirectional.bottomStart,
Padding( children: [
padding: EdgeInsets.only(
bottom:
isPinned && widget.showPinHighlight ? 8.0 : 0.0,
),
child: Column(
crossAxisAlignment: widget.reverse
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (widget.message.pinned &&
widget.message.pinnedBy != null &&
widget.showPinHighlight)
_buildPinnedMessage(widget.message),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
if (!widget.reverse &&
widget.showUserAvatar ==
DisplayWidget.show &&
widget.message.user != null) ...[
_buildUserAvatar(),
const SizedBox(width: 4),
],
if (widget.showUserAvatar == DisplayWidget.hide)
SizedBox(width: avatarWidth + 4),
Flexible(
child: PortalTarget(
visible: showReactions,
portalFollower: showReactions
? Container(
transform:
Matrix4.translationValues(
widget.reverse ? 12 : -12,
0,
0,
),
constraints: const BoxConstraints(
maxWidth: 22 * 6.0,
),
child: _buildReactionIndicator(
context,
),
)
: null,
anchor: Aligned(
follower: Alignment(
widget.reverse ? 1 : -1,
-1,
),
target: Alignment(
widget.reverse ? -1 : 1,
-1,
),
),
child: Stack(
clipBehavior: Clip.none,
children: [
Padding(
padding: widget.showReactions
? EdgeInsets.only(
top: widget
.message
.reactionCounts
?.isNotEmpty ==
true
? 18
: 0,
)
: EdgeInsets.zero,
child: (widget.message.isDeleted &&
!isFailedState)
? Container(
// ignore: lines_longer_than_80_chars
margin: EdgeInsets.symmetric(
horizontal:
// ignore: lines_longer_than_80_chars
widget.showUserAvatar ==
// ignore: lines_longer_than_80_chars
DisplayWidget
.gone
? 0
: 4.0,
),
child: StreamDeletedMessage(
borderRadiusGeometry: widget
.borderRadiusGeometry,
borderSide:
widget.borderSide,
shape: widget.shape,
messageTheme:
widget.messageTheme,
),
)
: Card(
clipBehavior: Clip.hardEdge,
elevation: 0,
margin: EdgeInsets.symmetric(
horizontal: (isFailedState
? 15.0
: 0.0) +
// ignore: lines_longer_than_80_chars
(widget.showUserAvatar ==
DisplayWidget
.gone
? 0
: 4.0),
),
shape: widget.shape ??
RoundedRectangleBorder(
side: widget
.borderSide ??
BorderSide(
color: widget
// ignore: lines_longer_than_80_chars
.messageTheme
// ignore: lines_longer_than_80_chars
.messageBorderColor ??
Colors.grey,
),
borderRadius: widget
// ignore: lines_longer_than_80_chars
.borderRadiusGeometry ??
BorderRadius.zero,
),
color: _getBackgroundColor(),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.end,
mainAxisSize:
MainAxisSize.min,
children: <Widget>[
if (hasQuotedMessage)
_buildQuotedMessage(),
if (hasNonUrlAttachments)
_parseAttachments(),
if (!isGiphy)
_buildTextBubble(),
],
),
),
),
if (widget.showReactionPickerIndicator)
Positioned(
right: widget.reverse ? null : 4,
left: widget.reverse ? 4 : null,
top: -8,
child: CustomPaint(
painter: ReactionBubblePainter(
_streamChatTheme
.colorTheme.barsBg,
Colors.transparent,
Colors.transparent,
tailCirclesSpace: 1,
),
),
),
],
),
),
),
if (widget.reverse &&
widget.showUserAvatar ==
DisplayWidget.show &&
widget.message.user != null) ...[
_buildUserAvatar(),
const SizedBox(width: 4),
],
],
),
if (showBottomRow)
SizedBox(height: context.textScaleFactor * 18.0),
],
),
),
if (showBottomRow)
Padding( Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: !widget.reverse ? bottomRowPadding : 0,
right: widget.reverse ? bottomRowPadding : 0,
bottom: bottom:
isPinned && widget.showPinHighlight ? 6.0 : 0.0, isPinned && widget.showPinHighlight ? 8.0 : 0.0,
),
child: Column(
crossAxisAlignment: widget.reverse
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (widget.message.pinned &&
widget.message.pinnedBy != null &&
widget.showPinHighlight)
_buildPinnedMessage(widget.message),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
if (!widget.reverse &&
widget.showUserAvatar ==
DisplayWidget.show &&
widget.message.user != null) ...[
_buildUserAvatar(),
const SizedBox(width: 4),
],
if (widget.showUserAvatar ==
DisplayWidget.hide)
SizedBox(width: avatarWidth + 4),
Flexible(
child: PortalTarget(
visible: showReactions,
portalFollower: showReactions
? Container(
transform:
Matrix4.translationValues(
widget.reverse ? 12 : -12,
0,
0,
),
constraints: const BoxConstraints(
maxWidth: 22 * 6.0,
),
child: _buildReactionIndicator(
context,
),
)
: null,
anchor: Aligned(
follower: Alignment(
widget.reverse ? 1 : -1,
-1,
),
target: Alignment(
widget.reverse ? -1 : 1,
-1,
),
),
child: Stack(
clipBehavior: Clip.none,
children: [
Padding(
padding: widget.showReactions
? EdgeInsets.only(
top: widget
.message
.reactionCounts
?.isNotEmpty ==
true
? 18
: 0,
)
: EdgeInsets.zero,
child: (widget.message.isDeleted &&
!isFailedState)
? Container(
margin:
EdgeInsets.symmetric(
horizontal:
// ignore: lines_longer_than_80_chars
widget.showUserAvatar ==
// ignore: lines_longer_than_80_chars
DisplayWidget.gone
? 0
: 4.0,
),
child: StreamDeletedMessage(
// ignore: lines_longer_than_80_chars
borderRadiusGeometry: widget
.borderRadiusGeometry,
borderSide:
widget.borderSide,
shape: widget.shape,
messageTheme:
widget.messageTheme,
),
)
: Card(
clipBehavior: Clip.hardEdge,
elevation: 0,
margin:
EdgeInsets.symmetric(
horizontal: (isFailedState
? 15.0
: 0.0) +
// ignore: lines_longer_than_80_chars
(widget.showUserAvatar ==
DisplayWidget
.gone
? 0
: 4.0),
),
shape: widget.shape ??
RoundedRectangleBorder(
side: widget
.borderSide ??
BorderSide(
color: widget
// ignore: lines_longer_than_80_chars
.messageTheme
// ignore: lines_longer_than_80_chars
.messageBorderColor ??
Colors.grey,
),
borderRadius: widget
// ignore: lines_longer_than_80_chars
.borderRadiusGeometry ??
BorderRadius.zero,
),
color: _backgroundColor,
child: Column(
crossAxisAlignment:
CrossAxisAlignment
.end,
mainAxisSize:
MainAxisSize.min,
children: <Widget>[
if (hasQuotedMessage)
_buildQuotedMessage(),
// ignore: lines_longer_than_80_chars
if (hasNonUrlAttachments)
_parseAttachments(),
if (!isGiphy)
_buildTextBubble(),
],
),
),
),
if (widget
.showReactionPickerIndicator)
Positioned(
right: widget.reverse ? null : 4,
left: widget.reverse ? 4 : null,
top: -8,
child: CustomPaint(
painter: ReactionBubblePainter(
_streamChatTheme
.colorTheme.barsBg,
Colors.transparent,
Colors.transparent,
tailCirclesSpace: 1,
),
),
),
],
),
),
),
if (widget.reverse &&
widget.showUserAvatar ==
DisplayWidget.show &&
widget.message.user != null) ...[
_buildUserAvatar(),
const SizedBox(width: 4),
],
],
),
if (showBottomRow)
SizedBox(
height: context.textScaleFactor * 18.0,
),
],
), ),
child: widget.bottomRowBuilder?.call(
context,
widget.message,
) ??
_bottomRow,
), ),
if (isFailedState) if (showBottomRow)
Positioned( Padding(
right: widget.reverse ? 0 : null, padding: EdgeInsets.only(
left: widget.reverse ? null : 0, left: !widget.reverse ? bottomRowPadding : 0,
bottom: showBottomRow ? 18 : -2, right: widget.reverse ? bottomRowPadding : 0,
child: StreamSvgIcon.error(size: 20), bottom: isPinned && widget.showPinHighlight
), ? 6.0
], : 0.0,
), ),
], child: widget.bottomRowBuilder?.call(
context,
widget.message,
) ??
_bottomRow,
),
if (isFailedState)
Positioned(
right: widget.reverse ? 0 : null,
left: widget.reverse ? null : 0,
bottom: showBottomRow ? 18 : -2,
child: StreamSvgIcon.error(size: 20),
),
],
),
],
),
), ),
), ),
), ),
@@ -1343,12 +1355,8 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
StreamSvgIcon.pin( StreamSvgIcon.pin(size: 16),
size: 16, const SizedBox(width: 4),
),
const SizedBox(
width: 4,
),
Text( Text(
context.translations.pinnedByUserText( context.translations.pinnedByUserText(
pinnedBy: pinnedBy, pinnedBy: pinnedBy,
@@ -1367,7 +1375,7 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
bool get isPinned => widget.message.pinned; bool get isPinned => widget.message.pinned;
Color? _getBackgroundColor() { Color? get _backgroundColor {
if (hasQuotedMessage) { if (hasQuotedMessage) {
return widget.messageTheme.messageBackgroundColor; return widget.messageTheme.messageBackgroundColor;
} }
@@ -109,8 +109,8 @@ class StreamChatState extends State<StreamChat> {
child: Builder( child: Builder(
builder: (context) { builder: (context) {
StreamChatClient.additionalHeaders = { StreamChatClient.additionalHeaders = {
'X-Stream-Client': 'X-Stream-Client': '${StreamChatClient.defaultUserAgent}-'
'${StreamChatClient.defaultUserAgent}-ui', 'ui-${StreamChatClient.packageVersion}',
}; };
return widget.child ?? const Offstage(); return widget.child ?? const Offstage();
}, },
@@ -9,7 +9,10 @@ import 'package:url_launcher/url_launcher.dart';
/// Launch URL /// Launch URL
Future<void> launchURL(BuildContext context, String url) async { Future<void> launchURL(BuildContext context, String url) async {
try { try {
await launchUrl(Uri.parse(url).withScheme); await launchUrl(
Uri.parse(url).withScheme,
mode: LaunchMode.externalApplication,
);
} catch (e) { } catch (e) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(context.translations.launchUrlError)), SnackBar(content: Text(context.translations.launchUrlError)),
@@ -282,25 +282,26 @@ class _StreamAttachmentPickerState extends State<StreamAttachmentPicker> {
.iconBuilder(context, _filePickerIndex == i + 1), .iconBuilder(context, _filePickerIndex == i + 1),
), ),
const Spacer(), const Spacer(),
FutureBuilder( if (widget.isOpen)
future: PhotoManager.requestPermissionExtend(), FutureBuilder(
builder: (context, snapshot) { future: PhotoManager.requestPermissionExtend(),
if (snapshot.hasData && builder: (context, snapshot) {
snapshot.data == PermissionState.limited) { if (snapshot.hasData &&
return TextButton( snapshot.data == PermissionState.limited) {
child: Text(context.translations.viewLibrary), return TextButton(
onPressed: () async { child: Text(context.translations.viewLibrary),
await PhotoManager.presentLimited(); onPressed: () async {
_mediaListViewController.updateMedia( await PhotoManager.presentLimited();
newValue: true, _mediaListViewController.updateMedia(
); newValue: true,
}, );
); },
} );
}
return const SizedBox.shrink(); return const SizedBox.shrink();
}, },
), ),
], ],
), ),
DecoratedBox( DecoratedBox(
@@ -70,7 +70,7 @@ typedef UserMentionTileBuilder = Widget Function(
/// Widget builder for action button. /// Widget builder for action button.
/// ///
/// [defaultActionButton] is the default [IconButton] configuration, /// [defaultActionButton] is the default [IconButton] configuration,
/// use [defaultActionButton.copyWith] to easily customize it. /// use .copyWith to easily customize it.
typedef ActionButtonBuilder = Widget Function( typedef ActionButtonBuilder = Widget Function(
BuildContext context, BuildContext context,
IconButton defaultActionButton, IconButton defaultActionButton,
@@ -998,7 +998,10 @@ class StreamMessageInputState extends State<StreamMessageInput>
_lastSearchedContainsUrlText = value; _lastSearchedContainsUrlText = value;
final matchedUrls = _urlRegex.allMatches(value).toList() final matchedUrls = _urlRegex.allMatches(value).toList()
..removeWhere((it) => it.group(0)?.split('.').last.isValidTLD() == false); ..removeWhere((it) {
final _parsedMatch = Uri.tryParse(it.group(0) ?? '')?.withScheme;
return _parsedMatch?.host.split('.').last.isValidTLD() == false;
});
// Reset the og attachment if the text doesn't contain any url // Reset the og attachment if the text doesn't contain any url
if (matchedUrls.isEmpty || if (matchedUrls.isEmpty ||
@@ -1221,7 +1224,7 @@ class StreamMessageInputState extends State<StreamMessageInput>
void _setCommand(Command c) { void _setCommand(Command c) {
_effectiveController _effectiveController
..clear() ..reset()
..command = c; ..command = c;
setState(() { setState(() {
_showCommandsOverlay = false; _showCommandsOverlay = false;
@@ -97,8 +97,9 @@ class _StreamVideoThumbnailImageState extends State<StreamVideoThumbnailImage> {
); );
} }
if (!snapshot.hasData) { if (!snapshot.hasData) {
return Container( return SizedBox(
constraints: const BoxConstraints.expand(), height: double.maxFinite,
width: double.maxFinite,
child: widget.placeholderBuilder?.call(context) ?? child: widget.placeholderBuilder?.call(context) ??
Shimmer.fromColors( Shimmer.fromColors(
baseColor: _streamChatTheme.colorTheme.disabled, baseColor: _streamChatTheme.colorTheme.disabled,
@@ -106,16 +107,22 @@ class _StreamVideoThumbnailImageState extends State<StreamVideoThumbnailImage> {
child: Image.asset( child: Image.asset(
'images/placeholder.png', 'images/placeholder.png',
fit: BoxFit.cover, fit: BoxFit.cover,
height: widget.height,
width: widget.width,
package: 'stream_chat_flutter', package: 'stream_chat_flutter',
), ),
), ),
); );
} }
return Image.memory( return SizedBox(
snapshot.data!, height: double.maxFinite,
fit: widget.fit, width: double.maxFinite,
height: widget.height, child: Image.memory(
width: widget.width, snapshot.data!,
fit: widget.fit,
height: widget.height,
width: widget.width,
),
); );
}, },
), ),
+3 -3
View File
@@ -1,7 +1,7 @@
name: stream_chat_flutter name: stream_chat_flutter
homepage: https://github.com/GetStream/stream-chat-flutter homepage: https://github.com/GetStream/stream-chat-flutter
description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter. description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter.
version: 4.2.0 version: 4.3.0
repository: https://github.com/GetStream/stream-chat-flutter repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
@@ -32,11 +32,11 @@ dependencies:
meta: ^1.3.0 meta: ^1.3.0
path_provider: ^2.0.1 path_provider: ^2.0.1
photo_manager: ^2.0.1 photo_manager: ^2.0.1
photo_view: ^0.13.0 photo_view: ^0.14.0
rxdart: ^0.27.0 rxdart: ^0.27.0
share_plus: ^4.0.1 share_plus: ^4.0.1
shimmer: ^2.0.0 shimmer: ^2.0.0
stream_chat_flutter_core: ^4.2.0 stream_chat_flutter_core: ^4.3.0
substring_highlight: ^1.0.26 substring_highlight: ^1.0.26
url_launcher: ^6.1.0 url_launcher: ^6.1.0
video_player: ^2.1.0 video_player: ^2.1.0
@@ -1,3 +1,7 @@
## 4.3.0
- Updated `stream_chat` dependency to [`4.3.0`](https://pub.dev/packages/stream_chat/changelog).
## 4.2.0 ## 4.2.0
- Updated `stream_chat` dependency to [`4.2.0`](https://pub.dev/packages/stream_chat/changelog). - Updated `stream_chat` dependency to [`4.2.0`](https://pub.dev/packages/stream_chat/changelog).
@@ -26,8 +26,7 @@ dependencies:
cupertino_icons: ^1.0.3 cupertino_icons: ^1.0.3
flutter: flutter:
sdk: flutter sdk: flutter
stream_chat_flutter_core: ^2.2.1 stream_chat_flutter_core: ^4.3.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
@@ -96,7 +96,8 @@ class StreamChatCoreState extends State<StreamChatCore>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
StreamChatClient.additionalHeaders = { StreamChatClient.additionalHeaders = {
'X-Stream-Client': '${StreamChatClient.defaultUserAgent}-core', 'X-Stream-Client': '${StreamChatClient.defaultUserAgent}-'
'core-${StreamChatClient.packageVersion}',
}; };
return widget.child; return widget.child;
} }
@@ -1,7 +1,7 @@
name: stream_chat_flutter_core name: stream_chat_flutter_core
homepage: https://github.com/GetStream/stream-chat-flutter homepage: https://github.com/GetStream/stream-chat-flutter
description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter. description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter.
version: 4.2.0 version: 4.3.0
repository: https://github.com/GetStream/stream-chat-flutter repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
@@ -17,8 +17,7 @@ dependencies:
freezed_annotation: ^2.0.3 freezed_annotation: ^2.0.3
meta: ^1.3.0 meta: ^1.3.0
rxdart: ^0.27.0 rxdart: ^0.27.0
stream_chat: ^4.2.0 stream_chat: ^4.3.0
dev_dependencies: dev_dependencies:
build_runner: ^2.0.1 build_runner: ^2.0.1
dart_code_metrics: ^4.4.0 dart_code_metrics: ^4.4.0
@@ -1,3 +1,9 @@
## 3.2.0
✅ Added
* Added support for `unreadMessagesSeparatorText` translation.
## 3.1.0 ## 3.1.0
* Added support for [German](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart) locale. * Added support for [German](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart) locale.
@@ -405,6 +405,14 @@ class NnStreamChatLocalizations extends GlobalStreamChatLocalizations {
@override @override
String get viewLibrary => 'View library'; String get viewLibrary => 'View library';
@override
String unreadMessagesSeparatorText(int unreadCount) {
if (unreadCount == 1) {
return '1 unread message';
}
return '$unreadCount unread messages';
}
} }
void main() async { void main() async {
@@ -163,7 +163,7 @@ class StreamChatLocalizationsDe extends GlobalStreamChatLocalizations {
String get allowGalleryAccessMessage => 'Zugang zu Ihrer Galerie gewähren'; String get allowGalleryAccessMessage => 'Zugang zu Ihrer Galerie gewähren';
@override @override
String get flagMessageLabel => 'Markierte Nachricht'; String get flagMessageLabel => 'Nachricht melden';
@override @override
String get flagMessageQuestion => String get flagMessageQuestion =>
@@ -171,13 +171,13 @@ class StreamChatLocalizationsDe extends GlobalStreamChatLocalizations {
'\nModerator für weitere Untersuchungen senden?'; '\nModerator für weitere Untersuchungen senden?';
@override @override
String get flagLabel => 'MARKIEREN'; String get flagLabel => 'MELDEN';
@override @override
String get cancelLabel => 'ABBRECHEN'; String get cancelLabel => 'ABBRECHEN';
@override @override
String get flagMessageSuccessfulLabel => 'Nachricht markiert'; String get flagMessageSuccessfulLabel => 'Nachricht gemeldet';
@override @override
String get flagMessageSuccessfulText => String get flagMessageSuccessfulText =>
@@ -283,7 +283,7 @@ class StreamChatLocalizationsDe extends GlobalStreamChatLocalizations {
String get streamChatLabel => 'Stream Chat'; String get streamChatLabel => 'Stream Chat';
@override @override
String get searchingForNetworkText => 'Searching for Network'; String get searchingForNetworkText => 'Netzwerk wird gesucht';
@override @override
String get offlineLabel => 'Offline...'; String get offlineLabel => 'Offline...';
@@ -380,5 +380,13 @@ class StreamChatLocalizationsDe extends GlobalStreamChatLocalizations {
'Sie sind nicht berechtigt Nachrichten zu senden'; 'Sie sind nicht berechtigt Nachrichten zu senden';
@override @override
String get viewLibrary => 'Bibliothek ansehen'; String get viewLibrary => 'Bibliothek öffnen';
@override
String unreadMessagesSeparatorText(int unreadCount) {
if (unreadCount == 1) {
return '1 ungelesene Nachricht';
}
return '$unreadCount ungelesene Nachrichten';
}
} }
@@ -381,4 +381,12 @@ class StreamChatLocalizationsEn extends GlobalStreamChatLocalizations {
@override @override
String get viewLibrary => 'View library'; String get viewLibrary => 'View library';
@override
String unreadMessagesSeparatorText(int unreadCount) {
if (unreadCount == 1) {
return '1 unread message';
}
return '$unreadCount unread messages';
}
} }
@@ -387,4 +387,12 @@ No es posible añadir más de $limit archivos adjuntos
@override @override
String get linkDisabledError => 'Los enlaces están deshabilitados'; String get linkDisabledError => 'Los enlaces están deshabilitados';
@override
String unreadMessagesSeparatorText(int unreadCount) {
if (unreadCount == 1) {
return '1 mensaje no leído';
}
return '$unreadCount mensajes no leídos';
}
} }
@@ -386,4 +386,12 @@ Limite de pièces jointes dépassée : il n'est pas possible d'ajouter plus de $
@override @override
String get linkDisabledError => 'Les liens sont désactivés'; String get linkDisabledError => 'Les liens sont désactivés';
@override
String unreadMessagesSeparatorText(int unreadCount) {
if (unreadCount == 1) {
return '1 message non lu';
}
return '$unreadCount messages non lus';
}
} }
@@ -380,4 +380,12 @@ class StreamChatLocalizationsHi extends GlobalStreamChatLocalizations {
@override @override
String get linkDisabledError => 'लिंक भेजना प्रतिबंधित'; String get linkDisabledError => 'लिंक भेजना प्रतिबंधित';
@override
String unreadMessagesSeparatorText(int unreadCount) {
if (unreadCount == 1) {
return '1 अपठित संदेश';
}
return '$unreadCount अपठित संदेश';
}
} }
@@ -40,7 +40,12 @@ class StreamChatLocalizationsIt extends GlobalStreamChatLocalizations {
String get onlyVisibleToYouText => 'Visible solo a te'; String get onlyVisibleToYouText => 'Visible solo a te';
@override @override
String threadReplyCountText(int count) => '$count risposte al thread'; String threadReplyCountText(int count) {
if (count == 1) {
return '1 risposta al thread';
}
return '$count risposte al thread';
}
@override @override
String attachmentsUploadProgressText({ String attachmentsUploadProgressText({
@@ -383,4 +388,12 @@ Attenzione: il limite massimo di $limit file è stato superato.
@override @override
String get linkDisabledError => 'I links sono disattivati'; String get linkDisabledError => 'I links sono disattivati';
@override
String unreadMessagesSeparatorText(int unreadCount) {
if (unreadCount == 1) {
return '1 messaggio non letto';
}
return '$unreadCount messaggi non letti';
}
} }
@@ -365,4 +365,12 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
@override @override
String get linkDisabledError => 'リンクが無効になっています'; String get linkDisabledError => 'リンクが無効になっています';
@override
String unreadMessagesSeparatorText(int unreadCount) {
if (unreadCount == 1) {
return '未読メッセージ1通';
}
return '$unreadCountつの未読メッセージ';
}
} }
@@ -366,4 +366,12 @@ class StreamChatLocalizationsKo extends GlobalStreamChatLocalizations {
@override @override
String get linkDisabledError => '링크가 비활성화되었습니다.'; String get linkDisabledError => '링크가 비활성화되었습니다.';
@override
String unreadMessagesSeparatorText(int unreadCount) {
if (unreadCount == 1) {
return '읽지 않은 메시지 1개';
}
return '읽지 않은 메시지 $unreadCount개';
}
} }
@@ -384,4 +384,12 @@ Não é possível adicionar mais de $limit arquivos de uma vez
@override @override
String get viewLibrary => 'Ver biblioteca'; String get viewLibrary => 'Ver biblioteca';
@override
String unreadMessagesSeparatorText(int unreadCount) {
if (unreadCount == 1) {
return '1 mensagem não lida';
}
return '$unreadCount mensagens não lidas';
}
} }
@@ -1,6 +1,6 @@
name: stream_chat_localizations name: stream_chat_localizations
description: The Official localizations for Stream Chat Flutter, a service for building chat applications description: The Official localizations for Stream Chat Flutter, a service for building chat applications
version: 3.1.0 version: 3.2.0
homepage: https://github.com/GetStream/stream-chat-flutter homepage: https://github.com/GetStream/stream-chat-flutter
repository: https://github.com/GetStream/stream-chat-flutter repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
@@ -14,8 +14,7 @@ dependencies:
sdk: flutter sdk: flutter
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
stream_chat_flutter: ^4.1.0 stream_chat_flutter: ^4.3.0
dev_dependencies: dev_dependencies:
dart_code_metrics: ^4.4.0 dart_code_metrics: ^4.4.0
flutter_test: flutter_test:
@@ -1,3 +1,7 @@
## 4.2.0
- Added support for `Channel.ownCapabilities`
## 4.1.0 ## 4.1.0
🔄 Changed 🔄 Changed
@@ -11,9 +11,8 @@ dependencies:
cupertino_icons: ^1.0.3 cupertino_icons: ^1.0.3
flutter: flutter:
sdk: flutter sdk: flutter
stream_chat: ^2.2.1 stream_chat: ^4.3.0
stream_chat_persistence: ^2.2.0 stream_chat_persistence: ^4.2.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
@@ -56,7 +56,7 @@ class DriftChatDatabase extends _$DriftChatDatabase {
// you should bump this number whenever you change or add a table definition. // you should bump this number whenever you change or add a table definition.
@override @override
int get schemaVersion => 8; int get schemaVersion => 9;
@override @override
MigrationStrategy get migration => MigrationStrategy( MigrationStrategy get migration => MigrationStrategy(
@@ -6,7 +6,7 @@ part of 'drift_chat_database.dart';
// MoorGenerator // MoorGenerator
// ************************************************************************** // **************************************************************************
// ignore_for_file: unnecessary_brace_in_string_interps, unnecessary_this // ignore_for_file: type=lint
class ChannelEntity extends DataClass implements Insertable<ChannelEntity> { class ChannelEntity extends DataClass implements Insertable<ChannelEntity> {
/// The id of this channel /// The id of this channel
final String id; final String id;
@@ -17,6 +17,9 @@ class ChannelEntity extends DataClass implements Insertable<ChannelEntity> {
/// The cid of this channel /// The cid of this channel
final String cid; final String cid;
/// List of user permissions on this channel
final List<String>? ownCapabilities;
/// The channel configuration data /// The channel configuration data
final Map<String, dynamic> config; final Map<String, dynamic> config;
@@ -47,6 +50,7 @@ class ChannelEntity extends DataClass implements Insertable<ChannelEntity> {
{required this.id, {required this.id,
required this.type, required this.type,
required this.cid, required this.cid,
this.ownCapabilities,
required this.config, required this.config,
required this.frozen, required this.frozen,
this.lastMessageAt, this.lastMessageAt,
@@ -65,7 +69,9 @@ class ChannelEntity extends DataClass implements Insertable<ChannelEntity> {
.mapFromDatabaseResponse(data['${effectivePrefix}type'])!, .mapFromDatabaseResponse(data['${effectivePrefix}type'])!,
cid: const StringType() cid: const StringType()
.mapFromDatabaseResponse(data['${effectivePrefix}cid'])!, .mapFromDatabaseResponse(data['${effectivePrefix}cid'])!,
config: $ChannelsTable.$converter0.mapToDart(const StringType() ownCapabilities: $ChannelsTable.$converter0.mapToDart(const StringType()
.mapFromDatabaseResponse(data['${effectivePrefix}own_capabilities'])),
config: $ChannelsTable.$converter1.mapToDart(const StringType()
.mapFromDatabaseResponse(data['${effectivePrefix}config']))!, .mapFromDatabaseResponse(data['${effectivePrefix}config']))!,
frozen: const BoolType() frozen: const BoolType()
.mapFromDatabaseResponse(data['${effectivePrefix}frozen'])!, .mapFromDatabaseResponse(data['${effectivePrefix}frozen'])!,
@@ -81,7 +87,7 @@ class ChannelEntity extends DataClass implements Insertable<ChannelEntity> {
.mapFromDatabaseResponse(data['${effectivePrefix}member_count'])!, .mapFromDatabaseResponse(data['${effectivePrefix}member_count'])!,
createdById: const StringType() createdById: const StringType()
.mapFromDatabaseResponse(data['${effectivePrefix}created_by_id']), .mapFromDatabaseResponse(data['${effectivePrefix}created_by_id']),
extraData: $ChannelsTable.$converter1.mapToDart(const StringType() extraData: $ChannelsTable.$converter2.mapToDart(const StringType()
.mapFromDatabaseResponse(data['${effectivePrefix}extra_data'])), .mapFromDatabaseResponse(data['${effectivePrefix}extra_data'])),
); );
} }
@@ -91,8 +97,13 @@ class ChannelEntity extends DataClass implements Insertable<ChannelEntity> {
map['id'] = Variable<String>(id); map['id'] = Variable<String>(id);
map['type'] = Variable<String>(type); map['type'] = Variable<String>(type);
map['cid'] = Variable<String>(cid); map['cid'] = Variable<String>(cid);
{ if (!nullToAbsent || ownCapabilities != null) {
final converter = $ChannelsTable.$converter0; final converter = $ChannelsTable.$converter0;
map['own_capabilities'] =
Variable<String?>(converter.mapToSql(ownCapabilities));
}
{
final converter = $ChannelsTable.$converter1;
map['config'] = Variable<String>(converter.mapToSql(config)!); map['config'] = Variable<String>(converter.mapToSql(config)!);
} }
map['frozen'] = Variable<bool>(frozen); map['frozen'] = Variable<bool>(frozen);
@@ -109,7 +120,7 @@ class ChannelEntity extends DataClass implements Insertable<ChannelEntity> {
map['created_by_id'] = Variable<String?>(createdById); map['created_by_id'] = Variable<String?>(createdById);
} }
if (!nullToAbsent || extraData != null) { if (!nullToAbsent || extraData != null) {
final converter = $ChannelsTable.$converter1; final converter = $ChannelsTable.$converter2;
map['extra_data'] = Variable<String?>(converter.mapToSql(extraData)); map['extra_data'] = Variable<String?>(converter.mapToSql(extraData));
} }
return map; return map;
@@ -122,6 +133,8 @@ class ChannelEntity extends DataClass implements Insertable<ChannelEntity> {
id: serializer.fromJson<String>(json['id']), id: serializer.fromJson<String>(json['id']),
type: serializer.fromJson<String>(json['type']), type: serializer.fromJson<String>(json['type']),
cid: serializer.fromJson<String>(json['cid']), cid: serializer.fromJson<String>(json['cid']),
ownCapabilities:
serializer.fromJson<List<String>?>(json['ownCapabilities']),
config: serializer.fromJson<Map<String, dynamic>>(json['config']), config: serializer.fromJson<Map<String, dynamic>>(json['config']),
frozen: serializer.fromJson<bool>(json['frozen']), frozen: serializer.fromJson<bool>(json['frozen']),
lastMessageAt: serializer.fromJson<DateTime?>(json['lastMessageAt']), lastMessageAt: serializer.fromJson<DateTime?>(json['lastMessageAt']),
@@ -140,6 +153,7 @@ class ChannelEntity extends DataClass implements Insertable<ChannelEntity> {
'id': serializer.toJson<String>(id), 'id': serializer.toJson<String>(id),
'type': serializer.toJson<String>(type), 'type': serializer.toJson<String>(type),
'cid': serializer.toJson<String>(cid), 'cid': serializer.toJson<String>(cid),
'ownCapabilities': serializer.toJson<List<String>?>(ownCapabilities),
'config': serializer.toJson<Map<String, dynamic>>(config), 'config': serializer.toJson<Map<String, dynamic>>(config),
'frozen': serializer.toJson<bool>(frozen), 'frozen': serializer.toJson<bool>(frozen),
'lastMessageAt': serializer.toJson<DateTime?>(lastMessageAt), 'lastMessageAt': serializer.toJson<DateTime?>(lastMessageAt),
@@ -156,6 +170,7 @@ class ChannelEntity extends DataClass implements Insertable<ChannelEntity> {
{String? id, {String? id,
String? type, String? type,
String? cid, String? cid,
Value<List<String>?> ownCapabilities = const Value.absent(),
Map<String, dynamic>? config, Map<String, dynamic>? config,
bool? frozen, bool? frozen,
Value<DateTime?> lastMessageAt = const Value.absent(), Value<DateTime?> lastMessageAt = const Value.absent(),
@@ -169,6 +184,9 @@ class ChannelEntity extends DataClass implements Insertable<ChannelEntity> {
id: id ?? this.id, id: id ?? this.id,
type: type ?? this.type, type: type ?? this.type,
cid: cid ?? this.cid, cid: cid ?? this.cid,
ownCapabilities: ownCapabilities.present
? ownCapabilities.value
: this.ownCapabilities,
config: config ?? this.config, config: config ?? this.config,
frozen: frozen ?? this.frozen, frozen: frozen ?? this.frozen,
lastMessageAt: lastMessageAt:
@@ -186,6 +204,7 @@ class ChannelEntity extends DataClass implements Insertable<ChannelEntity> {
..write('id: $id, ') ..write('id: $id, ')
..write('type: $type, ') ..write('type: $type, ')
..write('cid: $cid, ') ..write('cid: $cid, ')
..write('ownCapabilities: $ownCapabilities, ')
..write('config: $config, ') ..write('config: $config, ')
..write('frozen: $frozen, ') ..write('frozen: $frozen, ')
..write('lastMessageAt: $lastMessageAt, ') ..write('lastMessageAt: $lastMessageAt, ')
@@ -200,8 +219,20 @@ class ChannelEntity extends DataClass implements Insertable<ChannelEntity> {
} }
@override @override
int get hashCode => Object.hash(id, type, cid, config, frozen, lastMessageAt, int get hashCode => Object.hash(
createdAt, updatedAt, deletedAt, memberCount, createdById, extraData); id,
type,
cid,
ownCapabilities,
config,
frozen,
lastMessageAt,
createdAt,
updatedAt,
deletedAt,
memberCount,
createdById,
extraData);
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
@@ -209,6 +240,7 @@ class ChannelEntity extends DataClass implements Insertable<ChannelEntity> {
other.id == this.id && other.id == this.id &&
other.type == this.type && other.type == this.type &&
other.cid == this.cid && other.cid == this.cid &&
other.ownCapabilities == this.ownCapabilities &&
other.config == this.config && other.config == this.config &&
other.frozen == this.frozen && other.frozen == this.frozen &&
other.lastMessageAt == this.lastMessageAt && other.lastMessageAt == this.lastMessageAt &&
@@ -224,6 +256,7 @@ class ChannelsCompanion extends UpdateCompanion<ChannelEntity> {
final Value<String> id; final Value<String> id;
final Value<String> type; final Value<String> type;
final Value<String> cid; final Value<String> cid;
final Value<List<String>?> ownCapabilities;
final Value<Map<String, dynamic>> config; final Value<Map<String, dynamic>> config;
final Value<bool> frozen; final Value<bool> frozen;
final Value<DateTime?> lastMessageAt; final Value<DateTime?> lastMessageAt;
@@ -237,6 +270,7 @@ class ChannelsCompanion extends UpdateCompanion<ChannelEntity> {
this.id = const Value.absent(), this.id = const Value.absent(),
this.type = const Value.absent(), this.type = const Value.absent(),
this.cid = const Value.absent(), this.cid = const Value.absent(),
this.ownCapabilities = const Value.absent(),
this.config = const Value.absent(), this.config = const Value.absent(),
this.frozen = const Value.absent(), this.frozen = const Value.absent(),
this.lastMessageAt = const Value.absent(), this.lastMessageAt = const Value.absent(),
@@ -251,6 +285,7 @@ class ChannelsCompanion extends UpdateCompanion<ChannelEntity> {
required String id, required String id,
required String type, required String type,
required String cid, required String cid,
this.ownCapabilities = const Value.absent(),
required Map<String, dynamic> config, required Map<String, dynamic> config,
this.frozen = const Value.absent(), this.frozen = const Value.absent(),
this.lastMessageAt = const Value.absent(), this.lastMessageAt = const Value.absent(),
@@ -268,6 +303,7 @@ class ChannelsCompanion extends UpdateCompanion<ChannelEntity> {
Expression<String>? id, Expression<String>? id,
Expression<String>? type, Expression<String>? type,
Expression<String>? cid, Expression<String>? cid,
Expression<List<String>?>? ownCapabilities,
Expression<Map<String, dynamic>>? config, Expression<Map<String, dynamic>>? config,
Expression<bool>? frozen, Expression<bool>? frozen,
Expression<DateTime?>? lastMessageAt, Expression<DateTime?>? lastMessageAt,
@@ -282,6 +318,7 @@ class ChannelsCompanion extends UpdateCompanion<ChannelEntity> {
if (id != null) 'id': id, if (id != null) 'id': id,
if (type != null) 'type': type, if (type != null) 'type': type,
if (cid != null) 'cid': cid, if (cid != null) 'cid': cid,
if (ownCapabilities != null) 'own_capabilities': ownCapabilities,
if (config != null) 'config': config, if (config != null) 'config': config,
if (frozen != null) 'frozen': frozen, if (frozen != null) 'frozen': frozen,
if (lastMessageAt != null) 'last_message_at': lastMessageAt, if (lastMessageAt != null) 'last_message_at': lastMessageAt,
@@ -298,6 +335,7 @@ class ChannelsCompanion extends UpdateCompanion<ChannelEntity> {
{Value<String>? id, {Value<String>? id,
Value<String>? type, Value<String>? type,
Value<String>? cid, Value<String>? cid,
Value<List<String>?>? ownCapabilities,
Value<Map<String, dynamic>>? config, Value<Map<String, dynamic>>? config,
Value<bool>? frozen, Value<bool>? frozen,
Value<DateTime?>? lastMessageAt, Value<DateTime?>? lastMessageAt,
@@ -311,6 +349,7 @@ class ChannelsCompanion extends UpdateCompanion<ChannelEntity> {
id: id ?? this.id, id: id ?? this.id,
type: type ?? this.type, type: type ?? this.type,
cid: cid ?? this.cid, cid: cid ?? this.cid,
ownCapabilities: ownCapabilities ?? this.ownCapabilities,
config: config ?? this.config, config: config ?? this.config,
frozen: frozen ?? this.frozen, frozen: frozen ?? this.frozen,
lastMessageAt: lastMessageAt ?? this.lastMessageAt, lastMessageAt: lastMessageAt ?? this.lastMessageAt,
@@ -335,8 +374,13 @@ class ChannelsCompanion extends UpdateCompanion<ChannelEntity> {
if (cid.present) { if (cid.present) {
map['cid'] = Variable<String>(cid.value); map['cid'] = Variable<String>(cid.value);
} }
if (config.present) { if (ownCapabilities.present) {
final converter = $ChannelsTable.$converter0; final converter = $ChannelsTable.$converter0;
map['own_capabilities'] =
Variable<String?>(converter.mapToSql(ownCapabilities.value));
}
if (config.present) {
final converter = $ChannelsTable.$converter1;
map['config'] = Variable<String>(converter.mapToSql(config.value)!); map['config'] = Variable<String>(converter.mapToSql(config.value)!);
} }
if (frozen.present) { if (frozen.present) {
@@ -361,7 +405,7 @@ class ChannelsCompanion extends UpdateCompanion<ChannelEntity> {
map['created_by_id'] = Variable<String?>(createdById.value); map['created_by_id'] = Variable<String?>(createdById.value);
} }
if (extraData.present) { if (extraData.present) {
final converter = $ChannelsTable.$converter1; final converter = $ChannelsTable.$converter2;
map['extra_data'] = map['extra_data'] =
Variable<String?>(converter.mapToSql(extraData.value)); Variable<String?>(converter.mapToSql(extraData.value));
} }
@@ -374,6 +418,7 @@ class ChannelsCompanion extends UpdateCompanion<ChannelEntity> {
..write('id: $id, ') ..write('id: $id, ')
..write('type: $type, ') ..write('type: $type, ')
..write('cid: $cid, ') ..write('cid: $cid, ')
..write('ownCapabilities: $ownCapabilities, ')
..write('config: $config, ') ..write('config: $config, ')
..write('frozen: $frozen, ') ..write('frozen: $frozen, ')
..write('lastMessageAt: $lastMessageAt, ') ..write('lastMessageAt: $lastMessageAt, ')
@@ -409,12 +454,20 @@ class $ChannelsTable extends Channels
late final GeneratedColumn<String?> cid = GeneratedColumn<String?>( late final GeneratedColumn<String?> cid = GeneratedColumn<String?>(
'cid', aliasedName, false, 'cid', aliasedName, false,
type: const StringType(), requiredDuringInsert: true); type: const StringType(), requiredDuringInsert: true);
final VerificationMeta _ownCapabilitiesMeta =
const VerificationMeta('ownCapabilities');
@override
late final GeneratedColumnWithTypeConverter<List<String>, String?>
ownCapabilities = GeneratedColumn<String?>(
'own_capabilities', aliasedName, true,
type: const StringType(), requiredDuringInsert: false)
.withConverter<List<String>>($ChannelsTable.$converter0);
final VerificationMeta _configMeta = const VerificationMeta('config'); final VerificationMeta _configMeta = const VerificationMeta('config');
@override @override
late final GeneratedColumnWithTypeConverter<Map<String, dynamic>, String?> late final GeneratedColumnWithTypeConverter<Map<String, dynamic>, String?>
config = GeneratedColumn<String?>('config', aliasedName, false, config = GeneratedColumn<String?>('config', aliasedName, false,
type: const StringType(), requiredDuringInsert: true) type: const StringType(), requiredDuringInsert: true)
.withConverter<Map<String, dynamic>>($ChannelsTable.$converter0); .withConverter<Map<String, dynamic>>($ChannelsTable.$converter1);
final VerificationMeta _frozenMeta = const VerificationMeta('frozen'); final VerificationMeta _frozenMeta = const VerificationMeta('frozen');
@override @override
late final GeneratedColumn<bool?> frozen = GeneratedColumn<bool?>( late final GeneratedColumn<bool?> frozen = GeneratedColumn<bool?>(
@@ -467,12 +520,13 @@ class $ChannelsTable extends Channels
late final GeneratedColumnWithTypeConverter<Map<String, Object?>, String?> late final GeneratedColumnWithTypeConverter<Map<String, Object?>, String?>
extraData = GeneratedColumn<String?>('extra_data', aliasedName, true, extraData = GeneratedColumn<String?>('extra_data', aliasedName, true,
type: const StringType(), requiredDuringInsert: false) type: const StringType(), requiredDuringInsert: false)
.withConverter<Map<String, Object?>>($ChannelsTable.$converter1); .withConverter<Map<String, Object?>>($ChannelsTable.$converter2);
@override @override
List<GeneratedColumn> get $columns => [ List<GeneratedColumn> get $columns => [
id, id,
type, type,
cid, cid,
ownCapabilities,
config, config,
frozen, frozen,
lastMessageAt, lastMessageAt,
@@ -509,6 +563,7 @@ class $ChannelsTable extends Channels
} else if (isInserting) { } else if (isInserting) {
context.missing(_cidMeta); context.missing(_cidMeta);
} }
context.handle(_ownCapabilitiesMeta, const VerificationResult.success());
context.handle(_configMeta, const VerificationResult.success()); context.handle(_configMeta, const VerificationResult.success());
if (data.containsKey('frozen')) { if (data.containsKey('frozen')) {
context.handle(_frozenMeta, context.handle(_frozenMeta,
@@ -561,9 +616,11 @@ class $ChannelsTable extends Channels
return $ChannelsTable(attachedDatabase, alias); return $ChannelsTable(attachedDatabase, alias);
} }
static TypeConverter<Map<String, dynamic>, String> $converter0 = static TypeConverter<List<String>, String> $converter0 =
ListConverter<String>();
static TypeConverter<Map<String, dynamic>, String> $converter1 =
MapConverter(); MapConverter();
static TypeConverter<Map<String, Object?>, String> $converter1 = static TypeConverter<Map<String, Object?>, String> $converter2 =
MapConverter<Object?>(); MapConverter<Object?>();
} }
@@ -1,6 +1,6 @@
// coverage:ignore-file // coverage:ignore-file
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:stream_chat_persistence/src/converter/map_converter.dart'; import 'package:stream_chat_persistence/src/converter/converter.dart';
/// Represents a [Channels] table in [MoorChatDatabase]. /// Represents a [Channels] table in [MoorChatDatabase].
@DataClassName('ChannelEntity') @DataClassName('ChannelEntity')
@@ -14,6 +14,10 @@ class Channels extends Table {
/// The cid of this channel /// The cid of this channel
TextColumn get cid => text()(); TextColumn get cid => text()();
/// List of user permissions on this channel
TextColumn get ownCapabilities =>
text().nullable().map(ListConverter<String>())();
/// The channel configuration data /// The channel configuration data
TextColumn get config => text().map(MapConverter())(); TextColumn get config => text().map(MapConverter())();
@@ -8,6 +8,7 @@ extension ChannelEntityX on ChannelEntity {
final config = ChannelConfig.fromJson(this.config); final config = ChannelConfig.fromJson(this.config);
return ChannelModel( return ChannelModel(
id: id, id: id,
ownCapabilities: ownCapabilities,
config: config, config: config,
type: type, type: type,
frozen: frozen, frozen: frozen,
@@ -46,6 +47,7 @@ extension ChannelModelX on ChannelModel {
id: id, id: id,
type: type, type: type,
cid: cid, cid: cid,
ownCapabilities: ownCapabilities,
config: config.toJson(), config: config.toJson(),
frozen: frozen, frozen: frozen,
lastMessageAt: lastMessageAt, lastMessageAt: lastMessageAt,
@@ -1,7 +1,7 @@
name: stream_chat_persistence name: stream_chat_persistence
homepage: https://github.com/GetStream/stream-chat-flutter homepage: https://github.com/GetStream/stream-chat-flutter
description: Official Stream Chat Persistence library. Build your own chat experience using Dart and Flutter. description: Official Stream Chat Persistence library. Build your own chat experience using Dart and Flutter.
version: 4.1.0 version: 4.2.0
repository: https://github.com/GetStream/stream-chat-flutter repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
@@ -19,8 +19,7 @@ dependencies:
path: ^1.8.0 path: ^1.8.0
path_provider: ^2.0.1 path_provider: ^2.0.1
sqlite3_flutter_libs: ^0.5.0 sqlite3_flutter_libs: ^0.5.0
stream_chat: ^4.1.0 stream_chat: ^4.3.0
dev_dependencies: dev_dependencies:
build_runner: ^2.0.1 build_runner: ^2.0.1
dart_code_metrics: ^4.4.0 dart_code_metrics: ^4.4.0
@@ -14,6 +14,7 @@ void main() {
id: 'testId', id: 'testId',
type: 'testType', type: 'testType',
cid: 'testCid', cid: 'testCid',
ownCapabilities: ['testCapability'],
config: {'max_message_length': 33}, config: {'max_message_length': 33},
frozen: math.Random().nextBool(), frozen: math.Random().nextBool(),
lastMessageAt: DateTime.now(), lastMessageAt: DateTime.now(),
@@ -29,6 +30,7 @@ void main() {
final channelModel = entity.toChannelModel(createdBy: user); final channelModel = entity.toChannelModel(createdBy: user);
expect(channelModel, isA<ChannelModel>()); expect(channelModel, isA<ChannelModel>());
expect(channelModel.id, entity.id); expect(channelModel.id, entity.id);
expect(channelModel.ownCapabilities, entity.ownCapabilities);
expect(channelModel.config.toJson()['max_message_length'], 33); expect(channelModel.config.toJson()['max_message_length'], 33);
expect(channelModel.frozen, entity.frozen); expect(channelModel.frozen, entity.frozen);
expect(channelModel.createdAt, isSameDateAs(entity.createdAt)); expect(channelModel.createdAt, isSameDateAs(entity.createdAt));
@@ -68,6 +70,7 @@ void main() {
final channelModel = channelState.channel!; final channelModel = channelState.channel!;
expect(channelModel.id, entity.id); expect(channelModel.id, entity.id);
expect(channelModel.ownCapabilities, entity.ownCapabilities);
expect(channelModel.config.toJson()['max_message_length'], 33); expect(channelModel.config.toJson()['max_message_length'], 33);
expect(channelModel.frozen, entity.frozen); expect(channelModel.frozen, entity.frozen);
expect(channelModel.createdAt, isSameDateAs(entity.createdAt)); expect(channelModel.createdAt, isSameDateAs(entity.createdAt));
@@ -87,6 +90,7 @@ void main() {
id: 'testId', id: 'testId',
type: 'testType', type: 'testType',
cid: 'testCid', cid: 'testCid',
ownCapabilities: ['testCapability'],
config: ChannelConfig(maxMessageLength: 33), config: ChannelConfig(maxMessageLength: 33),
frozen: math.Random().nextBool(), frozen: math.Random().nextBool(),
lastMessageAt: DateTime.now(), lastMessageAt: DateTime.now(),
@@ -101,6 +105,7 @@ void main() {
final channelEntity = model.toEntity(); final channelEntity = model.toEntity();
expect(channelEntity, isA<ChannelEntity>()); expect(channelEntity, isA<ChannelEntity>());
expect(channelEntity.id, model.id); expect(channelEntity.id, model.id);
expect(channelEntity.ownCapabilities, model.ownCapabilities);
expect( expect(
channelEntity.config['max_message_length'], channelEntity.config['max_message_length'],
model.config.maxMessageLength, model.config.maxMessageLength,