Merge pull request #1584 from GetStream/release/v6.2.0
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
name: Dart Code Metrics
|
name: Dart Code Metrics
|
||||||
|
|
||||||
env:
|
env:
|
||||||
flutter_version: "3.7.0"
|
flutter_version: "3.10.0"
|
||||||
folders: "lib, test"
|
folders: "lib, test"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ name: stream_flutter_workflow
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
|
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
|
||||||
flutter_version: "3.7.0"
|
flutter_version: "3.10.0"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
# Reporting a Vulnerability
|
||||||
|
At Stream we are committed to the security of our Software. We appreciate your efforts in disclosing vulnerabilities responsibly and we will make every effort to acknowledge your contributions.
|
||||||
|
|
||||||
|
Report security vulnerabilities at the following email address:
|
||||||
|
```
|
||||||
|
[[email protected]](mailto:[email protected])
|
||||||
|
```
|
||||||
|
Alternatively it is also possible to open a new issue in the affected repository, tagging it with the `security` tag.
|
||||||
|
|
||||||
|
A team member will acknowledge the vulnerability and will follow-up with more detailed information. A representative of the security team will be in touch if more information is needed.
|
||||||
|
|
||||||
|
# Information to include in a report
|
||||||
|
While we appreciate any information that you are willing to provide, please make sure to include the following:
|
||||||
|
* Which repository is affected
|
||||||
|
* Which branch, if relevant
|
||||||
|
* Be as descriptive as possible, the team will replicate the vulnerability before working on a fix.
|
||||||
+2
-2
@@ -26,9 +26,9 @@ scripts:
|
|||||||
- Note: you can also rely on your IDEs Dart Analysis / Issues window.
|
- Note: you can also rely on your IDEs Dart Analysis / Issues window.
|
||||||
|
|
||||||
format:
|
format:
|
||||||
run: flutter format --set-exit-if-changed .
|
run: dart format --set-exit-if-changed .
|
||||||
description: |
|
description: |
|
||||||
Run `flutter format --set-exit-if-changed .` in all packages.
|
Run `dart format --set-exit-if-changed .` in all packages.
|
||||||
|
|
||||||
metrics:
|
metrics:
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
## 6.2.0
|
||||||
|
|
||||||
|
🐞 Fixed
|
||||||
|
|
||||||
|
- [[#1422]](https://github.com/GetStream/stream-chat-flutter/issues/1422) Fixed `User.createdAt` property using
|
||||||
|
currentTime when the ws connection is not established.
|
||||||
|
|
||||||
|
✅ Added
|
||||||
|
|
||||||
|
- Added support for `ChatPersistenceClient.isConnected` for checking if the client is connected to the database.
|
||||||
|
|
||||||
## 6.1.0
|
## 6.1.0
|
||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ class User extends Equatable {
|
|||||||
this.role,
|
this.role,
|
||||||
String? name,
|
String? name,
|
||||||
String? image,
|
String? image,
|
||||||
DateTime? createdAt,
|
this.createdAt,
|
||||||
DateTime? updatedAt,
|
this.updatedAt,
|
||||||
this.lastActive,
|
this.lastActive,
|
||||||
Map<String, Object?> extraData = const {},
|
Map<String, Object?> extraData = const {},
|
||||||
this.online = false,
|
this.online = false,
|
||||||
@@ -44,8 +44,7 @@ class User extends Equatable {
|
|||||||
this.banExpires,
|
this.banExpires,
|
||||||
this.teams = const [],
|
this.teams = const [],
|
||||||
this.language,
|
this.language,
|
||||||
}) : createdAt = createdAt ?? DateTime.now(),
|
}) :
|
||||||
updatedAt = updatedAt ?? DateTime.now(),
|
|
||||||
// For backwards compatibility, set 'name', 'image' in [extraData].
|
// For backwards compatibility, set 'name', 'image' in [extraData].
|
||||||
extraData = {
|
extraData = {
|
||||||
...extraData,
|
...extraData,
|
||||||
@@ -104,11 +103,11 @@ class User extends Equatable {
|
|||||||
|
|
||||||
/// Date of user creation.
|
/// Date of user creation.
|
||||||
@JsonKey(includeToJson: false)
|
@JsonKey(includeToJson: false)
|
||||||
final DateTime createdAt;
|
final DateTime? createdAt;
|
||||||
|
|
||||||
/// Date of last user update.
|
/// Date of last user update.
|
||||||
@JsonKey(includeToJson: false)
|
@JsonKey(includeToJson: false)
|
||||||
final DateTime updatedAt;
|
final DateTime? updatedAt;
|
||||||
|
|
||||||
/// Date of last user connection.
|
/// Date of last user connection.
|
||||||
@JsonKey(includeToJson: false)
|
@JsonKey(includeToJson: false)
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ import 'package:stream_chat/src/core/util/extension.dart';
|
|||||||
|
|
||||||
/// A simple client used for persisting chat data locally.
|
/// A simple client used for persisting chat data locally.
|
||||||
abstract class ChatPersistenceClient {
|
abstract class ChatPersistenceClient {
|
||||||
|
/// Whether the connection is established.
|
||||||
|
bool get isConnected;
|
||||||
|
|
||||||
/// Creates a new connection to the client
|
/// Creates a new connection to the client
|
||||||
Future<void> connect(String userId);
|
Future<void> connect(String userId);
|
||||||
|
|
||||||
|
|||||||
@@ -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 = '6.1.0';
|
const PACKAGE_VERSION = '6.2.0';
|
||||||
|
|||||||
@@ -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: 6.1.0
|
version: 6.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
|
||||||
|
|
||||||
|
|||||||
@@ -198,8 +198,8 @@ void main() {
|
|||||||
expect(user.banned, false);
|
expect(user.banned, false);
|
||||||
expect(user.teams, []);
|
expect(user.teams, []);
|
||||||
expect(user.lastActive, null);
|
expect(user.lastActive, null);
|
||||||
expect(user.createdAt, isNotNull);
|
expect(user.createdAt, null);
|
||||||
expect(user.updatedAt, isNotNull);
|
expect(user.updatedAt, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('default values, parse json', () {
|
test('default values, parse json', () {
|
||||||
@@ -214,8 +214,8 @@ void main() {
|
|||||||
expect(user.banned, false);
|
expect(user.banned, false);
|
||||||
expect(user.teams, []);
|
expect(user.teams, []);
|
||||||
expect(user.lastActive, null);
|
expect(user.lastActive, null);
|
||||||
expect(user.createdAt, isNotNull);
|
expect(user.createdAt, null);
|
||||||
expect(user.updatedAt, isNotNull);
|
expect(user.updatedAt, null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import 'package:stream_chat/src/db/chat_persistence_client.dart';
|
|||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
class TestPersistenceClient extends ChatPersistenceClient {
|
class TestPersistenceClient extends ChatPersistenceClient {
|
||||||
|
@override
|
||||||
|
bool get isConnected => throw UnimplementedError();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> connect(String userId) => throw UnimplementedError();
|
Future<void> connect(String userId) => throw UnimplementedError();
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,43 @@
|
|||||||
|
## 6.2.0
|
||||||
|
|
||||||
|
🐞 Fixed
|
||||||
|
|
||||||
|
- [[#1546]](https://github.com/GetStream/stream-chat-flutter/issues/1546)
|
||||||
|
Fixed `StreamMessageInputTheme.linkHighlightColor` returning null for default theme.
|
||||||
|
- [[#1548]](https://github.com/GetStream/stream-chat-flutter/issues/1548) Fixed `StreamMessageInput` urlRegex only
|
||||||
|
matching the lowercase `http(s)|ftp`.
|
||||||
|
- [[#1542]](https://github.com/GetStream/stream-chat-flutter/issues/1542) Handle error thrown in `StreamMessageInput`
|
||||||
|
when unable to fetch a link preview.
|
||||||
|
- [[#1540]](https://github.com/GetStream/stream-chat-flutter/issues/1540) Use `CircularProgressIndicator.adaptive`
|
||||||
|
instead of material indicator.
|
||||||
|
- [[#1490]](https://github.com/GetStream/stream-chat-flutter/issues/1490) Fixed `editMessageInputBuilder` property not
|
||||||
|
used in `MessageActionsModal.editMessage` option.
|
||||||
|
- [[#1544]](https://github.com/GetStream/stream-chat-flutter/issues/1544) Fixed error thrown when unable to fetch
|
||||||
|
image/data in Message link preview.
|
||||||
|
- [[#1482]](https://github.com/GetStream/stream-chat-flutter/issues/1482) Fixed `StreaChannelListTile` not showing
|
||||||
|
unread indicator when `currentUser` is not present in the initial member list.
|
||||||
|
- [[#1487]](https://github.com/GetStream/stream-chat-flutter/issues/1487) Use localized title
|
||||||
|
for `WebOrDesktopAttachmentPickerOption` in `StreamMessageInput`.
|
||||||
|
- [[#1250]](https://github.com/GetStream/stream-chat-flutter/issues/1250) Fixed bottomRow widgetSpans getting resized
|
||||||
|
twice when `textScaling` is enabled.
|
||||||
|
- [[#1498]](https://github.com/GetStream/stream-chat-flutter/issues/1498) Fixed `MessageInput` autocomplete not working
|
||||||
|
on non-mobile platforms.
|
||||||
|
- [[#1576]](https://github.com/GetStream/stream-chat-flutter/issues/1576) Temporary fix for `StreamMessageListView`
|
||||||
|
getting broken when loaded at a particular message and a new message is added.
|
||||||
|
|
||||||
|
✅ Added
|
||||||
|
|
||||||
|
- Added support for `StreamMessageThemeData.urlAttachmentTextMaxLine` to specify the `.maxLines` for the url attachment
|
||||||
|
text. [#1543](https://github.com/GetStream/stream-chat-flutter/issues/1543)
|
||||||
|
|
||||||
|
🔄 Changed
|
||||||
|
|
||||||
|
- Updated `shimmer` dependency to `^3.0.0`.
|
||||||
|
- Updated `image_gallery_saver` dependency to `^2.0.1`.
|
||||||
|
- Deprecated `ChannelPreview` in favor of `StreamChannelListTile`.
|
||||||
|
- Updated `stream_chat_flutter_core` dependency
|
||||||
|
to [`6.2.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
|
||||||
|
|
||||||
## 6.1.0
|
## 6.1.0
|
||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ import 'package:stream_chat_persistence/stream_chat_persistence.dart';
|
|||||||
|
|
||||||
final chatPersistentClient = StreamChatPersistenceClient(
|
final chatPersistentClient = StreamChatPersistenceClient(
|
||||||
logLevel: Level.INFO,
|
logLevel: Level.INFO,
|
||||||
connectionMode: ConnectionMode.background,
|
connectionMode: ConnectionMode.regular,
|
||||||
);
|
);
|
||||||
|
|
||||||
final client = StreamChatClient(
|
final client = StreamChatClient(
|
||||||
|
|||||||
@@ -128,12 +128,12 @@ class ChannelPage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Navigator(
|
Widget build(BuildContext context) => Navigator(
|
||||||
onGenerateRoute: (settings) => MaterialPageRoute(
|
onGenerateRoute: (settings) => MaterialPageRoute(
|
||||||
builder: (context) => Scaffold(
|
builder: (context) => const Scaffold(
|
||||||
appBar: const StreamChannelHeader(
|
appBar: StreamChannelHeader(
|
||||||
showBackButton: false,
|
showBackButton: false,
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: const <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: StreamMessageListView(),
|
child: StreamMessageListView(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -91,10 +91,10 @@ class ChannelPage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
// ignore: prefer_expression_function_bodies
|
// ignore: prefer_expression_function_bodies
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return const Scaffold(
|
||||||
appBar: const StreamChannelHeader(),
|
appBar: StreamChannelHeader(),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: const <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: StreamMessageListView(),
|
child: StreamMessageListView(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -126,10 +126,10 @@ class ChannelPage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return const Scaffold(
|
||||||
appBar: const StreamChannelHeader(),
|
appBar: StreamChannelHeader(),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: const <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: StreamMessageListView(),
|
child: StreamMessageListView(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -168,10 +168,10 @@ class ChannelPage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return const Scaffold(
|
||||||
appBar: const StreamChannelHeader(),
|
appBar: StreamChannelHeader(),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: const <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: StreamMessageListView(),
|
child: StreamMessageListView(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ dependencies:
|
|||||||
cupertino_icons: ^1.0.4
|
cupertino_icons: ^1.0.4
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
responsive_builder: ^0.6.4
|
responsive_builder: ^0.7.0
|
||||||
stream_chat_flutter:
|
stream_chat_flutter:
|
||||||
path: ../
|
path: ../
|
||||||
stream_chat_localizations:
|
stream_chat_localizations:
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ class _FileTypeImage extends StatelessWidget {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 20,
|
height: 20,
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator.adaptive(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -210,7 +210,7 @@ class _FileTypeImage extends StatelessWidget {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 20,
|
height: 20,
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator.adaptive(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ class StreamGiphyAttachment extends StreamAttachmentWidget {
|
|||||||
color: StreamChatTheme.of(context).colorTheme.barsBg,
|
color: StreamChatTheme.of(context).colorTheme.barsBg,
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topRight: Radius.circular(16),
|
topRight: Radius.circular(16),
|
||||||
@@ -115,7 +116,7 @@ class StreamGiphyAttachment extends StreamAttachmentWidget {
|
|||||||
width: constraints?.maxHeight,
|
width: constraints?.maxHeight,
|
||||||
height: constraints?.maxWidth,
|
height: constraints?.maxWidth,
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator.adaptive(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
imageUrl: imageUrl,
|
imageUrl: imageUrl,
|
||||||
@@ -241,10 +242,7 @@ class StreamGiphyAttachment extends StreamAttachmentWidget {
|
|||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
const Align(
|
const Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: Padding(
|
child: StreamVisibleFootnote(),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
||||||
child: StreamVisibleFootnote(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:shimmer/shimmer.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// {@template streamUrlAttachment}
|
/// {@template streamUrlAttachment}
|
||||||
@@ -64,14 +65,33 @@ class StreamUrlAttachment extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
CachedNetworkImage(
|
AspectRatio(
|
||||||
width: double.infinity,
|
// Default aspect ratio for Open Graph images.
|
||||||
imageUrl: urlAttachment.imageUrl!,
|
// https://www.kapwing.com/resources/what-is-an-og-image-make-and-format-og-images-for-your-blog-or-webpage
|
||||||
fit: BoxFit.cover,
|
aspectRatio: 1.91 / 1,
|
||||||
|
child: CachedNetworkImage(
|
||||||
|
imageUrl: urlAttachment.imageUrl!,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
placeholder: (context, __) {
|
||||||
|
final image = Image.asset(
|
||||||
|
'images/placeholder.png',
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
package: 'stream_chat_flutter',
|
||||||
|
);
|
||||||
|
final colorTheme =
|
||||||
|
StreamChatTheme.of(context).colorTheme;
|
||||||
|
return Shimmer.fromColors(
|
||||||
|
baseColor: colorTheme.disabled,
|
||||||
|
highlightColor: colorTheme.inputBg,
|
||||||
|
child: image,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
errorWidget: (_, __, ___) => const AttachmentError(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 0,
|
left: 0,
|
||||||
bottom: -1,
|
bottom: 0,
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
@@ -83,7 +103,8 @@ class StreamUrlAttachment extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
top: 8,
|
top: 8,
|
||||||
left: 8,
|
left: 8,
|
||||||
right: 8,
|
right: 12,
|
||||||
|
bottom: 4,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
hostDisplayName,
|
hostDisplayName,
|
||||||
@@ -99,20 +120,40 @@ class StreamUrlAttachment extends StatelessWidget {
|
|||||||
padding: textPadding,
|
padding: textPadding,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
if (urlAttachment.title != null)
|
if (urlAttachment.title != null)
|
||||||
Text(
|
Builder(builder: (context) {
|
||||||
urlAttachment.title!.trim(),
|
final maxLines = messageTheme.urlAttachmentTitleMaxLine;
|
||||||
maxLines: messageTheme.urlAttachmentTitleMaxLine ?? 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
TextOverflow? overflow;
|
||||||
style: messageTheme.urlAttachmentTitleStyle,
|
if (maxLines != null && maxLines > 0) {
|
||||||
),
|
overflow = TextOverflow.ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Text(
|
||||||
|
urlAttachment.title!.trim(),
|
||||||
|
maxLines: maxLines,
|
||||||
|
overflow: overflow,
|
||||||
|
style: messageTheme.urlAttachmentTitleStyle,
|
||||||
|
);
|
||||||
|
}),
|
||||||
if (urlAttachment.text != null)
|
if (urlAttachment.text != null)
|
||||||
Text(
|
Builder(builder: (context) {
|
||||||
urlAttachment.text!,
|
final maxLines = messageTheme.urlAttachmentTextMaxLine;
|
||||||
style: messageTheme.urlAttachmentTextStyle,
|
|
||||||
),
|
TextOverflow? overflow;
|
||||||
],
|
if (maxLines != null && maxLines > 0) {
|
||||||
|
overflow = TextOverflow.ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Text(
|
||||||
|
urlAttachment.text!,
|
||||||
|
maxLines: maxLines,
|
||||||
|
overflow: overflow,
|
||||||
|
style: messageTheme.urlAttachmentTextStyle,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
].insertBetween(const SizedBox(height: 4)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
+4
-2
@@ -345,9 +345,11 @@ class AttachmentActionsModal extends StatelessWidget {
|
|||||||
child: Stack(
|
child: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator.adaptive(
|
||||||
strokeWidth: 8,
|
strokeWidth: 8,
|
||||||
color: theme.colorTheme.accentPrimary,
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
|
theme.colorTheme.accentPrimary,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|||||||
@@ -507,10 +507,12 @@ class _StreamAutocompleteState extends State<StreamAutocomplete> {
|
|||||||
final anchor = widget.optionsAlignment._toAnchor();
|
final anchor = widget.optionsAlignment._toAnchor();
|
||||||
final shouldShowOptions = _shouldShowOptions;
|
final shouldShowOptions = _shouldShowOptions;
|
||||||
final optionViewBuilder = shouldShowOptions
|
final optionViewBuilder = shouldShowOptions
|
||||||
? _currentTrigger!.optionsViewBuilder(
|
? TextFieldTapRegion(
|
||||||
context,
|
child: _currentTrigger!.optionsViewBuilder(
|
||||||
_currentQuery!,
|
context,
|
||||||
_messageEditingController,
|
_currentQuery!,
|
||||||
|
_messageEditingController,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ class _ConnectingTitleState extends StatelessWidget {
|
|||||||
height: 16,
|
height: 16,
|
||||||
width: 16,
|
width: 16,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator.adaptive(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ class _ConnectingTitleState extends StatelessWidget {
|
|||||||
height: 16,
|
height: 16,
|
||||||
width: 16,
|
width: 16,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator.adaptive(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|||||||
/// The UI is rendered based on the first ancestor of type [StreamChatTheme].
|
/// The UI is rendered based on the first ancestor of type [StreamChatTheme].
|
||||||
/// Modify it to change the widget's appearance.
|
/// Modify it to change the widget's appearance.
|
||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
|
@Deprecated('Use StreamChannelListTile instead.')
|
||||||
class ChannelPreview extends StatelessWidget {
|
class ChannelPreview extends StatelessWidget {
|
||||||
/// {@macro channelPreview}
|
/// {@macro channelPreview}
|
||||||
const ChannelPreview({
|
const ChannelPreview({
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ class _FullScreenMediaState extends State<StreamFullScreenMedia> {
|
|||||||
final controller = videoPackages[attachment.id]!;
|
final controller = videoPackages[attachment.id]!;
|
||||||
if (!controller.initialized) {
|
if (!controller.initialized) {
|
||||||
return const Center(
|
return const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator.adaptive(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return InkWell(
|
return InkWell(
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class StreamUploadProgressIndicator extends StatelessWidget {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
height: 16,
|
height: 16,
|
||||||
width: 16,
|
width: 16,
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator.adaptive(
|
||||||
strokeWidth: 3,
|
strokeWidth: 3,
|
||||||
valueColor: AlwaysStoppedAnimation(progressIndicatorColor),
|
valueColor: AlwaysStoppedAnimation(progressIndicatorColor),
|
||||||
),
|
),
|
||||||
|
|||||||
+2
-1
@@ -400,8 +400,9 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
builder: (context) => EditMessageSheet(
|
builder: (context) => EditMessageSheet(
|
||||||
message: widget.message,
|
|
||||||
channel: channel,
|
channel: channel,
|
||||||
|
message: widget.message,
|
||||||
|
editMessageInputBuilder: widget.editMessageInputBuilder,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -792,19 +792,19 @@ Widget webOrDesktopAttachmentPickerBuilder({
|
|||||||
key: 'image-picker',
|
key: 'image-picker',
|
||||||
type: AttachmentPickerType.images,
|
type: AttachmentPickerType.images,
|
||||||
icon: StreamSvgIcon.pictures(size: 36).toIconThemeSvgIcon(),
|
icon: StreamSvgIcon.pictures(size: 36).toIconThemeSvgIcon(),
|
||||||
title: 'Upload a photo',
|
title: context.translations.uploadAPhotoLabel,
|
||||||
),
|
),
|
||||||
WebOrDesktopAttachmentPickerOption(
|
WebOrDesktopAttachmentPickerOption(
|
||||||
key: 'video-picker',
|
key: 'video-picker',
|
||||||
type: AttachmentPickerType.videos,
|
type: AttachmentPickerType.videos,
|
||||||
icon: StreamSvgIcon.record(size: 36).toIconThemeSvgIcon(),
|
icon: StreamSvgIcon.record(size: 36).toIconThemeSvgIcon(),
|
||||||
title: 'Upload a video',
|
title: context.translations.uploadAVideoLabel,
|
||||||
),
|
),
|
||||||
WebOrDesktopAttachmentPickerOption(
|
WebOrDesktopAttachmentPickerOption(
|
||||||
key: 'file-picker',
|
key: 'file-picker',
|
||||||
type: AttachmentPickerType.files,
|
type: AttachmentPickerType.files,
|
||||||
icon: StreamSvgIcon.files(size: 36).toIconThemeSvgIcon(),
|
icon: StreamSvgIcon.files(size: 36).toIconThemeSvgIcon(),
|
||||||
title: 'Upload a file',
|
title: context.translations.uploadAFileLabel,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
onOptionTap: (context, controller, option) async {
|
onOptionTap: (context, controller, option) async {
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ class _ParseAttachments extends StatelessWidget {
|
|||||||
width: size.width,
|
width: size.width,
|
||||||
height: size.height,
|
height: size.height,
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator.adaptive(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -394,7 +394,7 @@ class _VideoAttachmentThumbnailState extends State<_VideoAttachmentThumbnail> {
|
|||||||
width: 32,
|
width: 32,
|
||||||
child: _controller.value.isInitialized
|
child: _controller.value.isInitialized
|
||||||
? VideoPlayer(_controller)
|
? VideoPlayer(_controller)
|
||||||
: const CircularProgressIndicator(),
|
: const CircularProgressIndicator.adaptive(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1042,6 +1042,7 @@ class StreamMessageInputState extends State<StreamMessageInput>
|
|||||||
CancelableOperation? _enrichUrlOperation;
|
CancelableOperation? _enrichUrlOperation;
|
||||||
final _urlRegex = RegExp(
|
final _urlRegex = RegExp(
|
||||||
r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+',
|
r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+',
|
||||||
|
caseSensitive: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
void _checkContainsUrl(String value, BuildContext context) async {
|
void _checkContainsUrl(String value, BuildContext context) async {
|
||||||
@@ -1103,8 +1104,12 @@ class StreamMessageInputState extends State<StreamMessageInput>
|
|||||||
var response = _ogAttachmentCache[url];
|
var response = _ogAttachmentCache[url];
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
final client = StreamChat.of(context).client;
|
final client = StreamChat.of(context).client;
|
||||||
response = await client.enrichUrl(url);
|
try {
|
||||||
_ogAttachmentCache[url] = response;
|
response = await client.enrichUrl(url);
|
||||||
|
_ogAttachmentCache[url] = response;
|
||||||
|
} catch (e, stk) {
|
||||||
|
return Future.error(e, stk);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -1367,10 +1372,10 @@ class StreamMessageInputState extends State<StreamMessageInput>
|
|||||||
}
|
}
|
||||||
|
|
||||||
final streamChannel = StreamChannel.of(context);
|
final streamChannel = StreamChannel.of(context);
|
||||||
|
final channel = streamChannel.channel;
|
||||||
var message = _effectiveController.value;
|
var message = _effectiveController.value;
|
||||||
|
|
||||||
if (!streamChannel.channel.ownCapabilities
|
if (!channel.ownCapabilities.contains(PermissionType.sendLinks) &&
|
||||||
.contains(PermissionType.sendLinks) &&
|
|
||||||
_urlRegex.allMatches(message.text ?? '').any((element) =>
|
_urlRegex.allMatches(message.text ?? '').any((element) =>
|
||||||
element.group(0)?.split('.').last.isValidTLD() == true)) {
|
element.group(0)?.split('.').last.isValidTLD() == true)) {
|
||||||
showInfoBottomSheet(
|
showInfoBottomSheet(
|
||||||
@@ -1396,8 +1401,8 @@ class StreamMessageInputState extends State<StreamMessageInput>
|
|||||||
final skipEnrichUrl = _effectiveController.ogAttachment == null;
|
final skipEnrichUrl = _effectiveController.ogAttachment == null;
|
||||||
|
|
||||||
var shouldKeepFocus = widget.shouldKeepFocusAfterMessage;
|
var shouldKeepFocus = widget.shouldKeepFocusAfterMessage;
|
||||||
|
|
||||||
shouldKeepFocus ??= !_commandEnabled;
|
shouldKeepFocus ??= !_commandEnabled;
|
||||||
|
|
||||||
widget.onQuotedMessageCleared?.call();
|
widget.onQuotedMessageCleared?.call();
|
||||||
|
|
||||||
_effectiveController.reset();
|
_effectiveController.reset();
|
||||||
@@ -1406,12 +1411,35 @@ class StreamMessageInputState extends State<StreamMessageInput>
|
|||||||
message = await widget.preMessageSending!(message);
|
message = await widget.preMessageSending!(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
final channel = streamChannel.channel;
|
message = message.replaceMentionsWithId();
|
||||||
|
|
||||||
|
// If the channel is not up to date, we should reload it before sending
|
||||||
|
// the message.
|
||||||
if (!channel.state!.isUpToDate) {
|
if (!channel.state!.isUpToDate) {
|
||||||
await streamChannel.reloadChannel();
|
await streamChannel.reloadChannel();
|
||||||
|
|
||||||
|
// We need to wait for the frame to be rendered with the updated channel
|
||||||
|
// state before sending the message.
|
||||||
|
await WidgetsBinding.instance.endOfFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
message = message.replaceMentionsWithId();
|
await _sendOrUpdateMessage(
|
||||||
|
message: message,
|
||||||
|
skipEnrichUrl: skipEnrichUrl,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (shouldKeepFocus) {
|
||||||
|
FocusScope.of(context).requestFocus(_effectiveFocusNode);
|
||||||
|
} else {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _sendOrUpdateMessage({
|
||||||
|
required Message message,
|
||||||
|
bool skipEnrichUrl = false,
|
||||||
|
}) async {
|
||||||
|
final channel = StreamChannel.of(context).channel;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Future sendingFuture;
|
Future sendingFuture;
|
||||||
@@ -1427,12 +1455,6 @@ class StreamMessageInputState extends State<StreamMessageInput>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldKeepFocus) {
|
|
||||||
FocusScope.of(context).requestFocus(_effectiveFocusNode);
|
|
||||||
} else {
|
|
||||||
FocusScope.of(context).unfocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
final resp = await sendingFuture;
|
final resp = await sendingFuture;
|
||||||
if (resp.message?.type == 'error') {
|
if (resp.message?.type == 'error') {
|
||||||
_effectiveController.message = message;
|
_effectiveController.message = message;
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class LoadingIndicator extends StatelessWidget {
|
|||||||
const Center(
|
const Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(8),
|
padding: EdgeInsets.all(8),
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator.adaptive(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
|||||||
messageFilter: widget.messageFilter,
|
messageFilter: widget.messageFilter,
|
||||||
loadingBuilder: widget.loadingBuilder ??
|
loadingBuilder: widget.loadingBuilder ??
|
||||||
(context) => const Center(
|
(context) => const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator.adaptive(),
|
||||||
),
|
),
|
||||||
emptyBuilder: widget.emptyBuilder ??
|
emptyBuilder: widget.emptyBuilder ??
|
||||||
(context) => Center(
|
(context) => Center(
|
||||||
@@ -566,21 +566,33 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
|||||||
reverse: widget.reverse,
|
reverse: widget.reverse,
|
||||||
shrinkWrap: widget.shrinkWrap,
|
shrinkWrap: widget.shrinkWrap,
|
||||||
itemCount: itemCount,
|
itemCount: itemCount,
|
||||||
findChildIndexCallback: (Key key) {
|
|
||||||
final indexedKey = key as IndexedKey;
|
// Commented out as it is not working as expected.
|
||||||
final valueKey = indexedKey.key as ValueKey<String>?;
|
// The list view gets broken in the following case:
|
||||||
if (valueKey != null) {
|
// * The list view is loaded at a particular message (eg: Last Read, or a quoted message)
|
||||||
final index = messagesIndex[valueKey.value];
|
// and a new message is added to the list view.
|
||||||
if (index != null) {
|
//
|
||||||
// The calculation is as follows:
|
// Issues faced:
|
||||||
// * Add 2 to the index retrieved to account for the footer and the bottom loader.
|
// * https://github.com/GetStream/stream-chat-flutter/issues/1576
|
||||||
// * Multiply the result by 2 to account for the separators between each pair of items.
|
// * https://github.com/GetStream/stream-chat-flutter/issues/1414
|
||||||
// * Subtract 1 to adjust for the 0-based indexing of the list view.
|
//
|
||||||
return ((index + 2) * 2) - 1;
|
// Related issues: https://github.com/flutter/flutter/issues/107123
|
||||||
}
|
//
|
||||||
}
|
// findChildIndexCallback: (Key key) {
|
||||||
return null;
|
// final indexedKey = key as IndexedKey;
|
||||||
},
|
// final valueKey = indexedKey.key as ValueKey<String>?;
|
||||||
|
// if (valueKey != null) {
|
||||||
|
// final index = messagesIndex[valueKey.value];
|
||||||
|
// if (index != null) {
|
||||||
|
// // The calculation is as follows:
|
||||||
|
// // * Add 2 to the index retrieved to account for the footer and the bottom loader.
|
||||||
|
// // * Multiply the result by 2 to account for the separators between each pair of items.
|
||||||
|
// // * Subtract 1 to adjust for the 0-based indexing of the list view.
|
||||||
|
// return ((index + 2) * 2) - 1;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return null;
|
||||||
|
// },
|
||||||
|
|
||||||
// Item Count -> 8 (1 parent, 2 header+footer, 2 top+bottom, 3 messages)
|
// Item Count -> 8 (1 parent, 2 header+footer, 2 top+bottom, 3 messages)
|
||||||
// eg: |Type| rev(|Index(item)|) rev(|Index(separator)|) |Index(item)| |Index(separator)|
|
// eg: |Type| rev(|Index(item)|) rev(|Index(separator)|) |Index(item)| |Index(separator)|
|
||||||
@@ -899,7 +911,10 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
|||||||
final hasUrlAttachment =
|
final hasUrlAttachment =
|
||||||
message.attachments.any((it) => it.ogScrapeUrl != null);
|
message.attachments.any((it) => it.ogScrapeUrl != null);
|
||||||
|
|
||||||
final borderSide = isOnlyEmoji || hasUrlAttachment ? BorderSide.none : null;
|
final isEphemeral = message.isEphemeral;
|
||||||
|
|
||||||
|
final borderSide =
|
||||||
|
isOnlyEmoji || hasUrlAttachment || isEphemeral ? BorderSide.none : null;
|
||||||
|
|
||||||
final defaultMessageWidget = StreamMessageWidget(
|
final defaultMessageWidget = StreamMessageWidget(
|
||||||
showReplyMessage: false,
|
showReplyMessage: false,
|
||||||
@@ -980,24 +995,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
|||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
backgroundColor: _streamTheme.colorTheme.barsBg,
|
backgroundColor: _streamTheme.colorTheme.barsBg,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (unreadCount > 0) {
|
return scrollToBottomDefaultTapAction(unreadCount);
|
||||||
streamChannel!.channel.markRead();
|
|
||||||
}
|
|
||||||
if (!_upToDate) {
|
|
||||||
_bottomPaginationActive = false;
|
|
||||||
initialAlignment = 0;
|
|
||||||
initialIndex = 0;
|
|
||||||
await streamChannel!.reloadChannel();
|
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
_scrollController!.jumpTo(index: 0);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
_showScrollToBottom.value = false;
|
|
||||||
_scrollController!.jumpTo(
|
|
||||||
index: 0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
child: widget.reverse
|
child: widget.reverse
|
||||||
? StreamSvgIcon.down(
|
? StreamSvgIcon.down(
|
||||||
@@ -1101,10 +1099,13 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
|||||||
final showThreadReplyIndicator = !_isThreadConversation && hasReplies;
|
final showThreadReplyIndicator = !_isThreadConversation && hasReplies;
|
||||||
final isOnlyEmoji = message.text?.isOnlyEmoji ?? false;
|
final isOnlyEmoji = message.text?.isOnlyEmoji ?? false;
|
||||||
|
|
||||||
|
final isEphemeral = message.isEphemeral;
|
||||||
|
|
||||||
final hasUrlAttachment =
|
final hasUrlAttachment =
|
||||||
message.attachments.any((it) => it.ogScrapeUrl != null);
|
message.attachments.any((it) => it.ogScrapeUrl != null);
|
||||||
|
|
||||||
final borderSide = isOnlyEmoji || hasUrlAttachment ? BorderSide.none : null;
|
final borderSide =
|
||||||
|
isOnlyEmoji || hasUrlAttachment || isEphemeral ? BorderSide.none : null;
|
||||||
|
|
||||||
final currentUser = StreamChat.of(context).currentUser;
|
final currentUser = StreamChat.of(context).currentUser;
|
||||||
final members = StreamChannel.of(context).channel.state?.members ?? [];
|
final members = StreamChannel.of(context).channel.state?.members ?? [];
|
||||||
|
|||||||
@@ -153,8 +153,6 @@ class BottomRow extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final children = <WidgetSpan>[];
|
|
||||||
|
|
||||||
final threadParticipants = message.threadParticipants?.take(2);
|
final threadParticipants = message.threadParticipants?.take(2);
|
||||||
final showThreadParticipants = threadParticipants?.isNotEmpty == true;
|
final showThreadParticipants = threadParticipants?.isNotEmpty == true;
|
||||||
final replyCount = message.replyCount;
|
final replyCount = message.replyCount;
|
||||||
@@ -183,75 +181,69 @@ class BottomRow extends StatelessWidget {
|
|||||||
|
|
||||||
const usernameKey = Key('username');
|
const usernameKey = Key('username');
|
||||||
|
|
||||||
children.addAll([
|
final children = [
|
||||||
if (showUsername)
|
if (showUsername)
|
||||||
WidgetSpan(
|
usernameBuilder?.call(context, message) ??
|
||||||
child: usernameBuilder?.call(context, message) ??
|
Username(
|
||||||
Username(
|
key: usernameKey,
|
||||||
key: usernameKey,
|
message: message,
|
||||||
message: message,
|
messageTheme: messageTheme,
|
||||||
messageTheme: messageTheme,
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
if (showTimeStamp)
|
if (showTimeStamp)
|
||||||
WidgetSpan(
|
Text(
|
||||||
child: Text(
|
Jiffy(message.createdAt.toLocal()).jm,
|
||||||
Jiffy(message.createdAt.toLocal()).jm,
|
style: messageTheme.createdAtStyle,
|
||||||
style: messageTheme.createdAtStyle,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (showSendingIndicator)
|
if (showSendingIndicator)
|
||||||
WidgetSpan(
|
sendingIndicatorBuilder?.call(context, message) ??
|
||||||
child: sendingIndicatorBuilder?.call(context, message) ??
|
SendingIndicatorBuilder(
|
||||||
SendingIndicatorBuilder(
|
messageTheme: messageTheme,
|
||||||
messageTheme: messageTheme,
|
message: message,
|
||||||
message: message,
|
hasNonUrlAttachments: hasNonUrlAttachments,
|
||||||
hasNonUrlAttachments: hasNonUrlAttachments,
|
streamChat: streamChat,
|
||||||
streamChat: streamChat,
|
streamChatTheme: streamChatTheme,
|
||||||
streamChatTheme: streamChatTheme,
|
),
|
||||||
),
|
];
|
||||||
),
|
|
||||||
]);
|
|
||||||
|
|
||||||
final showThreadTail = !(hasUrlAttachments || isGiphy || isOnlyEmoji) &&
|
final showThreadTail = !(hasUrlAttachments || isGiphy || isOnlyEmoji) &&
|
||||||
(showThreadReplyIndicator || showInChannel);
|
(showThreadReplyIndicator || showInChannel);
|
||||||
|
|
||||||
final threadIndicatorWidgets = <WidgetSpan>[
|
final threadIndicatorWidgets = [
|
||||||
if (showThreadTail)
|
if (showThreadTail)
|
||||||
WidgetSpan(
|
// Added builder to use the nearest context to get the right
|
||||||
child: Padding(
|
// textScaleFactor value.
|
||||||
padding: EdgeInsets.only(
|
Builder(
|
||||||
bottom: context.textScaleFactor *
|
builder: (context) {
|
||||||
((messageTheme.repliesStyle?.fontSize ?? 1) / 2),
|
return Padding(
|
||||||
),
|
padding: EdgeInsets.only(
|
||||||
child: CustomPaint(
|
bottom: context.textScaleFactor *
|
||||||
size: const Size(16, 32) * context.textScaleFactor,
|
((messageTheme.repliesStyle?.fontSize ?? 1) / 2),
|
||||||
painter: ThreadReplyPainter(
|
|
||||||
context: context,
|
|
||||||
color: messageTheme.messageBorderColor,
|
|
||||||
reverse: reverse,
|
|
||||||
),
|
),
|
||||||
),
|
child: CustomPaint(
|
||||||
),
|
size: const Size(16, 32) * context.textScaleFactor,
|
||||||
|
painter: ThreadReplyPainter(
|
||||||
|
context: context,
|
||||||
|
color: messageTheme.messageBorderColor,
|
||||||
|
reverse: reverse,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
if (showInChannel || showThreadReplyIndicator) ...[
|
if (showInChannel || showThreadReplyIndicator) ...[
|
||||||
if (showThreadParticipants)
|
if (showThreadParticipants)
|
||||||
WidgetSpan(
|
SizedBox.fromSize(
|
||||||
child: SizedBox.fromSize(
|
size: Size((threadParticipants!.length * 8.0) + 8, 16),
|
||||||
size: Size((threadParticipants!.length * 8.0) + 8, 16),
|
child: ThreadParticipants(
|
||||||
child: ThreadParticipants(
|
threadParticipants: threadParticipants,
|
||||||
threadParticipants: threadParticipants,
|
streamChatTheme: streamChatTheme,
|
||||||
streamChatTheme: streamChatTheme,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
WidgetSpan(
|
MouseRegion(
|
||||||
child: MouseRegion(
|
cursor: SystemMouseCursors.click,
|
||||||
cursor: SystemMouseCursors.click,
|
child: GestureDetector(
|
||||||
child: GestureDetector(
|
onTap: _onThreadTap,
|
||||||
onTap: _onThreadTap,
|
child: Text(msg, style: messageTheme.repliesStyle),
|
||||||
child: Text(msg, style: messageTheme.repliesStyle),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -266,8 +258,21 @@ class BottomRow extends StatelessWidget {
|
|||||||
return Text.rich(
|
return Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: [
|
||||||
...children,
|
...children.insertBetween(const SizedBox(width: 8)).map((child) {
|
||||||
].insertBetween(const WidgetSpan(child: SizedBox(width: 8))),
|
final mediaQueryData = MediaQuery.of(context);
|
||||||
|
return WidgetSpan(
|
||||||
|
child: MediaQuery(
|
||||||
|
// Hardcoding the textScaleFactor to 1 to avoid the multiple
|
||||||
|
// resizing of the text. This is needed because the
|
||||||
|
// textScaleFactor is already applied to the textSpan.
|
||||||
|
//
|
||||||
|
// issue: https://github.com/GetStream/stream-chat-flutter/issues/1250
|
||||||
|
data: mediaQueryData.copyWith(textScaleFactor: 1),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
textAlign: reverse ? TextAlign.right : TextAlign.left,
|
textAlign: reverse ? TextAlign.right : TextAlign.left,
|
||||||
|
|||||||
@@ -217,17 +217,9 @@ class StreamMessageWidget extends StatefulWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
'giphy': (context, message, attachments) {
|
'giphy': (context, message, attachments) {
|
||||||
final border = RoundedRectangleBorder(
|
final attachmentWidget = Column(
|
||||||
side: attachmentBorderSide ??
|
children: [
|
||||||
BorderSide(
|
...attachments.map((attachment) {
|
||||||
color: StreamChatTheme.of(context).colorTheme.borders,
|
|
||||||
),
|
|
||||||
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
|
||||||
);
|
|
||||||
|
|
||||||
return WrapAttachmentWidget(
|
|
||||||
attachmentWidget: Column(
|
|
||||||
children: attachments.map((attachment) {
|
|
||||||
final mediaQueryData = MediaQuery.of(context);
|
final mediaQueryData = MediaQuery.of(context);
|
||||||
return StreamGiphyAttachment(
|
return StreamGiphyAttachment(
|
||||||
attachment: attachment,
|
attachment: attachment,
|
||||||
@@ -240,14 +232,25 @@ class StreamMessageWidget extends StatefulWidget {
|
|||||||
onShowMessage: onShowMessage,
|
onShowMessage: onShowMessage,
|
||||||
onReplyMessage: onReplyTap,
|
onReplyMessage: onReplyTap,
|
||||||
onAttachmentTap: onAttachmentTap != null
|
onAttachmentTap: onAttachmentTap != null
|
||||||
? () {
|
? () => onAttachmentTap(message, attachment)
|
||||||
onAttachmentTap(message, attachment);
|
|
||||||
}
|
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
}).toList(),
|
}),
|
||||||
),
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// If the message is ephemeral, we don't want to show the border.
|
||||||
|
if (message.isEphemeral) return attachmentWidget;
|
||||||
|
|
||||||
|
final color = StreamChatTheme.of(context).colorTheme.borders;
|
||||||
|
final border = RoundedRectangleBorder(
|
||||||
|
side: attachmentBorderSide ?? BorderSide(color: color),
|
||||||
|
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
||||||
|
);
|
||||||
|
|
||||||
|
return WrapAttachmentWidget(
|
||||||
attachmentShape: border,
|
attachmentShape: border,
|
||||||
|
attachmentWidget: attachmentWidget,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'file': (context, message, attachments) {
|
'file': (context, message, attachments) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
typedef ReactionIconBuilder = Widget Function(
|
typedef ReactionIconBuilder = Widget Function(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
|
// ignore: avoid_positional_boolean_parameters
|
||||||
bool isHighlighted,
|
bool isHighlighted,
|
||||||
double iconSize,
|
double iconSize,
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-2
@@ -196,8 +196,7 @@ class StreamChannelListTile extends StatelessWidget {
|
|||||||
initialData: channelState.members,
|
initialData: channelState.members,
|
||||||
comparator: const ListEquality().equals,
|
comparator: const ListEquality().equals,
|
||||||
builder: (context, members) {
|
builder: (context, members) {
|
||||||
if (members.isEmpty ||
|
if (members.isEmpty) {
|
||||||
!members.any((it) => it.user!.id == currentUser.id)) {
|
|
||||||
return const Offstage();
|
return const Offstage();
|
||||||
}
|
}
|
||||||
return unreadIndicatorBuilder?.call(context) ??
|
return unreadIndicatorBuilder?.call(context) ??
|
||||||
|
|||||||
-38
@@ -380,41 +380,3 @@ class StreamChannelListSeparator extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A widget that is used to display an error screen
|
|
||||||
/// when [StreamChannelListController] fails to load initial channels.
|
|
||||||
class StreamChannelListErrorWidget extends StatelessWidget {
|
|
||||||
/// Creates a new instance of [StreamChannelListErrorWidget] widget.
|
|
||||||
const StreamChannelListErrorWidget({
|
|
||||||
super.key,
|
|
||||||
this.onPressed,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// The callback to invoke when the user taps on the retry button.
|
|
||||||
final VoidCallback? onPressed;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
Text.rich(
|
|
||||||
TextSpan(
|
|
||||||
children: [
|
|
||||||
const WidgetSpan(
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.only(right: 2),
|
|
||||||
child: Icon(Icons.error_outline),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(text: context.translations.loadingChannelsError),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: onPressed,
|
|
||||||
child: Text(context.translations.retryLabel),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
+3
-3
@@ -202,9 +202,9 @@ class MediaThumbnailProvider extends ImageProvider<MediaThumbnailProvider> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ImageStreamCompleter loadBuffer(
|
ImageStreamCompleter loadImage(
|
||||||
MediaThumbnailProvider key,
|
MediaThumbnailProvider key,
|
||||||
DecoderBufferCallback decode,
|
ImageDecoderCallback decode,
|
||||||
) {
|
) {
|
||||||
return MultiFrameImageStreamCompleter(
|
return MultiFrameImageStreamCompleter(
|
||||||
codec: _loadAsync(key, decode),
|
codec: _loadAsync(key, decode),
|
||||||
@@ -221,7 +221,7 @@ class MediaThumbnailProvider extends ImageProvider<MediaThumbnailProvider> {
|
|||||||
|
|
||||||
Future<ui.Codec> _loadAsync(
|
Future<ui.Codec> _loadAsync(
|
||||||
MediaThumbnailProvider key,
|
MediaThumbnailProvider key,
|
||||||
DecoderBufferCallback decode,
|
ImageDecoderCallback decode,
|
||||||
) async {
|
) async {
|
||||||
assert(key == this, '$key is not $this');
|
assert(key == this, '$key is not $this');
|
||||||
final bytes = await media.thumbnailDataWithSize(
|
final bytes = await media.thumbnailDataWithSize(
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class StreamColorTheme {
|
|||||||
this.appBg = const Color(0xff070A0D),
|
this.appBg = const Color(0xff070A0D),
|
||||||
this.barsBg = const Color(0xff101418),
|
this.barsBg = const Color(0xff101418),
|
||||||
this.linkBg = const Color(0xff00193D),
|
this.linkBg = const Color(0xff00193D),
|
||||||
this.accentPrimary = const Color(0xff005FFF),
|
this.accentPrimary = const Color(0xff337eff),
|
||||||
this.accentError = const Color(0xffFF3742),
|
this.accentError = const Color(0xffFF3742),
|
||||||
this.accentInfo = const Color(0xff20E070),
|
this.accentInfo = const Color(0xff20E070),
|
||||||
this.borderTop = const Effect(
|
this.borderTop = const Effect(
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ class StreamMessageInputThemeData with Diagnosticable {
|
|||||||
sendButtonColor: sendButtonColor ?? this.sendButtonColor,
|
sendButtonColor: sendButtonColor ?? this.sendButtonColor,
|
||||||
actionButtonIdleColor:
|
actionButtonIdleColor:
|
||||||
actionButtonIdleColor ?? this.actionButtonIdleColor,
|
actionButtonIdleColor ?? this.actionButtonIdleColor,
|
||||||
|
linkHighlightColor: linkHighlightColor ?? this.linkHighlightColor,
|
||||||
expandButtonColor: expandButtonColor ?? this.expandButtonColor,
|
expandButtonColor: expandButtonColor ?? this.expandButtonColor,
|
||||||
inputTextStyle: inputTextStyle ?? this.inputTextStyle,
|
inputTextStyle: inputTextStyle ?? this.inputTextStyle,
|
||||||
sendButtonIdleColor: sendButtonIdleColor ?? this.sendButtonIdleColor,
|
sendButtonIdleColor: sendButtonIdleColor ?? this.sendButtonIdleColor,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat_flutter/src/theme/avatar_theme.dart';
|
import 'package:stream_chat_flutter/src/theme/avatar_theme.dart';
|
||||||
@@ -27,6 +29,7 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
this.urlAttachmentTitleStyle,
|
this.urlAttachmentTitleStyle,
|
||||||
this.urlAttachmentTextStyle,
|
this.urlAttachmentTextStyle,
|
||||||
this.urlAttachmentTitleMaxLine,
|
this.urlAttachmentTitleMaxLine,
|
||||||
|
this.urlAttachmentTextMaxLine,
|
||||||
}) : urlAttachmentBackgroundColor =
|
}) : urlAttachmentBackgroundColor =
|
||||||
urlAttachmentBackgroundColor ?? linkBackgroundColor;
|
urlAttachmentBackgroundColor ?? linkBackgroundColor;
|
||||||
|
|
||||||
@@ -82,6 +85,9 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
/// Max number of lines in Url link title.
|
/// Max number of lines in Url link title.
|
||||||
final int? urlAttachmentTitleMaxLine;
|
final int? urlAttachmentTitleMaxLine;
|
||||||
|
|
||||||
|
/// Max number of lines in Url link text.
|
||||||
|
final int? urlAttachmentTextMaxLine;
|
||||||
|
|
||||||
/// Copy with a theme
|
/// Copy with a theme
|
||||||
StreamMessageThemeData copyWith({
|
StreamMessageThemeData copyWith({
|
||||||
TextStyle? messageTextStyle,
|
TextStyle? messageTextStyle,
|
||||||
@@ -102,6 +108,7 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
TextStyle? urlAttachmentTitleStyle,
|
TextStyle? urlAttachmentTitleStyle,
|
||||||
TextStyle? urlAttachmentTextStyle,
|
TextStyle? urlAttachmentTextStyle,
|
||||||
int? urlAttachmentTitleMaxLine,
|
int? urlAttachmentTitleMaxLine,
|
||||||
|
int? urlAttachmentTextMaxLine,
|
||||||
}) {
|
}) {
|
||||||
return StreamMessageThemeData(
|
return StreamMessageThemeData(
|
||||||
messageTextStyle: messageTextStyle ?? this.messageTextStyle,
|
messageTextStyle: messageTextStyle ?? this.messageTextStyle,
|
||||||
@@ -128,6 +135,8 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
urlAttachmentTextStyle ?? this.urlAttachmentTextStyle,
|
urlAttachmentTextStyle ?? this.urlAttachmentTextStyle,
|
||||||
urlAttachmentTitleMaxLine:
|
urlAttachmentTitleMaxLine:
|
||||||
urlAttachmentTitleMaxLine ?? this.urlAttachmentTitleMaxLine,
|
urlAttachmentTitleMaxLine ?? this.urlAttachmentTitleMaxLine,
|
||||||
|
urlAttachmentTextMaxLine:
|
||||||
|
urlAttachmentTextMaxLine ?? this.urlAttachmentTextMaxLine,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +187,16 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
b.urlAttachmentTitleStyle,
|
b.urlAttachmentTitleStyle,
|
||||||
t,
|
t,
|
||||||
),
|
),
|
||||||
|
urlAttachmentTitleMaxLine: lerpDouble(
|
||||||
|
a.urlAttachmentTitleMaxLine,
|
||||||
|
b.urlAttachmentTitleMaxLine,
|
||||||
|
t,
|
||||||
|
)?.round(),
|
||||||
|
urlAttachmentTextMaxLine: lerpDouble(
|
||||||
|
a.urlAttachmentTextMaxLine,
|
||||||
|
b.urlAttachmentTextMaxLine,
|
||||||
|
t,
|
||||||
|
)?.round(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,6 +225,7 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
urlAttachmentTitleStyle: other.urlAttachmentTitleStyle,
|
urlAttachmentTitleStyle: other.urlAttachmentTitleStyle,
|
||||||
urlAttachmentTextStyle: other.urlAttachmentTextStyle,
|
urlAttachmentTextStyle: other.urlAttachmentTextStyle,
|
||||||
urlAttachmentTitleMaxLine: other.urlAttachmentTitleMaxLine,
|
urlAttachmentTitleMaxLine: other.urlAttachmentTitleMaxLine,
|
||||||
|
urlAttachmentTextMaxLine: other.urlAttachmentTextMaxLine,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +249,8 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
urlAttachmentHostStyle == other.urlAttachmentHostStyle &&
|
urlAttachmentHostStyle == other.urlAttachmentHostStyle &&
|
||||||
urlAttachmentTitleStyle == other.urlAttachmentTitleStyle &&
|
urlAttachmentTitleStyle == other.urlAttachmentTitleStyle &&
|
||||||
urlAttachmentTextStyle == other.urlAttachmentTextStyle &&
|
urlAttachmentTextStyle == other.urlAttachmentTextStyle &&
|
||||||
urlAttachmentTitleMaxLine == other.urlAttachmentTitleMaxLine;
|
urlAttachmentTitleMaxLine == other.urlAttachmentTitleMaxLine &&
|
||||||
|
urlAttachmentTextMaxLine == other.urlAttachmentTextMaxLine;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
@@ -248,7 +269,8 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
urlAttachmentHostStyle.hashCode ^
|
urlAttachmentHostStyle.hashCode ^
|
||||||
urlAttachmentTitleStyle.hashCode ^
|
urlAttachmentTitleStyle.hashCode ^
|
||||||
urlAttachmentTextStyle.hashCode ^
|
urlAttachmentTextStyle.hashCode ^
|
||||||
urlAttachmentTitleMaxLine.hashCode;
|
urlAttachmentTitleMaxLine.hashCode ^
|
||||||
|
urlAttachmentTextMaxLine.hashCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
@@ -284,6 +306,10 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
..add(DiagnosticsProperty(
|
..add(DiagnosticsProperty(
|
||||||
'urlAttachmentTitleMaxLine',
|
'urlAttachmentTitleMaxLine',
|
||||||
urlAttachmentTitleMaxLine,
|
urlAttachmentTitleMaxLine,
|
||||||
|
))
|
||||||
|
..add(DiagnosticsProperty(
|
||||||
|
'urlAttachmentTextMaxLine',
|
||||||
|
urlAttachmentTextMaxLine,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,15 +195,12 @@ class StreamChatThemeData {
|
|||||||
width: 32,
|
width: 32,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
messageLinksStyle: TextStyle(
|
messageLinksStyle: TextStyle(color: accentColor),
|
||||||
color: accentColor,
|
|
||||||
),
|
|
||||||
urlAttachmentBackgroundColor: colorTheme.linkBg,
|
urlAttachmentBackgroundColor: colorTheme.linkBg,
|
||||||
urlAttachmentHostStyle: textTheme.bodyBold.copyWith(color: accentColor),
|
urlAttachmentHostStyle: textTheme.bodyBold.copyWith(color: accentColor),
|
||||||
urlAttachmentTitleStyle:
|
urlAttachmentTitleStyle: textTheme.footnoteBold,
|
||||||
textTheme.body.copyWith(fontWeight: FontWeight.w700),
|
urlAttachmentTextStyle: textTheme.footnote,
|
||||||
urlAttachmentTextStyle:
|
urlAttachmentTitleMaxLine: 1,
|
||||||
textTheme.body.copyWith(fontWeight: FontWeight.w400),
|
|
||||||
),
|
),
|
||||||
otherMessageTheme: StreamMessageThemeData(
|
otherMessageTheme: StreamMessageThemeData(
|
||||||
reactionsBackgroundColor: colorTheme.borders,
|
reactionsBackgroundColor: colorTheme.borders,
|
||||||
@@ -215,9 +212,7 @@ class StreamChatThemeData {
|
|||||||
messageAuthorStyle:
|
messageAuthorStyle:
|
||||||
textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis),
|
textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis),
|
||||||
repliesStyle: textTheme.footnoteBold.copyWith(color: accentColor),
|
repliesStyle: textTheme.footnoteBold.copyWith(color: accentColor),
|
||||||
messageLinksStyle: TextStyle(
|
messageLinksStyle: TextStyle(color: accentColor),
|
||||||
color: accentColor,
|
|
||||||
),
|
|
||||||
messageBackgroundColor: colorTheme.barsBg,
|
messageBackgroundColor: colorTheme.barsBg,
|
||||||
messageBorderColor: colorTheme.borders,
|
messageBorderColor: colorTheme.borders,
|
||||||
avatarTheme: StreamAvatarThemeData(
|
avatarTheme: StreamAvatarThemeData(
|
||||||
@@ -229,10 +224,9 @@ class StreamChatThemeData {
|
|||||||
),
|
),
|
||||||
urlAttachmentBackgroundColor: colorTheme.linkBg,
|
urlAttachmentBackgroundColor: colorTheme.linkBg,
|
||||||
urlAttachmentHostStyle: textTheme.bodyBold.copyWith(color: accentColor),
|
urlAttachmentHostStyle: textTheme.bodyBold.copyWith(color: accentColor),
|
||||||
urlAttachmentTitleStyle:
|
urlAttachmentTitleStyle: textTheme.footnoteBold,
|
||||||
textTheme.body.copyWith(fontWeight: FontWeight.w700),
|
urlAttachmentTextStyle: textTheme.footnote,
|
||||||
urlAttachmentTextStyle:
|
urlAttachmentTitleMaxLine: 1,
|
||||||
textTheme.body.copyWith(fontWeight: FontWeight.w400),
|
|
||||||
),
|
),
|
||||||
messageInputTheme: StreamMessageInputThemeData(
|
messageInputTheme: StreamMessageInputThemeData(
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
|||||||
bool get isWeb => CurrentPlatform.isWeb;
|
bool get isWeb => CurrentPlatform.isWeb;
|
||||||
|
|
||||||
/// Returns true if the app is running in a mobile device.
|
/// Returns true if the app is running in a mobile device.
|
||||||
bool get isMobileDevice => true;
|
bool get isMobileDevice => CurrentPlatform.isIos || CurrentPlatform.isAndroid;
|
||||||
|
|
||||||
/// Returns true if the app is running in a desktop device.
|
/// Returns true if the app is running in a desktop device.
|
||||||
bool get isDesktopDevice =>
|
bool get isDesktopDevice =>
|
||||||
@@ -22,7 +22,7 @@ bool get isDesktopVideoPlayerSupported =>
|
|||||||
bool get isMobileDeviceOrWeb => isWeb || isMobileDevice;
|
bool get isMobileDeviceOrWeb => isWeb || isMobileDevice;
|
||||||
|
|
||||||
/// Returns true if the app is running in a desktop or web.
|
/// Returns true if the app is running in a desktop or web.
|
||||||
bool get isDesktopDeviceOrWeb => false;
|
bool get isDesktopDeviceOrWeb => isWeb || isDesktopDevice;
|
||||||
|
|
||||||
/// Returns true if the app is running in a flutter test environment.
|
/// Returns true if the app is running in a flutter test environment.
|
||||||
bool get isTestEnvironment => CurrentPlatform.isFlutterTest;
|
bool get isTestEnvironment => CurrentPlatform.isFlutterTest;
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ typedef ChannelInfoCallback = void Function(Channel);
|
|||||||
/// {@template channelPreviewBuilder}
|
/// {@template channelPreviewBuilder}
|
||||||
/// Builder used to create a custom [ChannelPreview] for a [Channel]
|
/// Builder used to create a custom [ChannelPreview] for a [Channel]
|
||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
|
@Deprecated('Use StreamChannelListViewIndexedWidgetBuilder instead')
|
||||||
typedef ChannelPreviewBuilder = Widget Function(BuildContext, Channel);
|
typedef ChannelPreviewBuilder = Widget Function(BuildContext, Channel);
|
||||||
|
|
||||||
/// {@template viewInfoCallback}
|
/// {@template viewInfoCallback}
|
||||||
@@ -348,6 +349,7 @@ typedef KeyEventPredicate = bool Function(FocusNode, KeyEvent);
|
|||||||
/// {@template userItemBuilder}
|
/// {@template userItemBuilder}
|
||||||
/// Builder used to create a custom [ListUserItem] from a [User]
|
/// Builder used to create a custom [ListUserItem] from a [User]
|
||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
|
// ignore: avoid_positional_boolean_parameters
|
||||||
typedef UserItemBuilder = Widget Function(BuildContext, User, bool);
|
typedef UserItemBuilder = Widget Function(BuildContext, User, bool);
|
||||||
|
|
||||||
/// The action to perform when the "scroll to bottom" button is pressed
|
/// The action to perform when the "scroll to bottom" button is pressed
|
||||||
|
|||||||
@@ -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: 6.1.0
|
version: 6.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
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ dependencies:
|
|||||||
flutter_portal: ^1.0.0
|
flutter_portal: ^1.0.0
|
||||||
flutter_svg: ^2.0.4
|
flutter_svg: ^2.0.4
|
||||||
http_parser: ^4.0.0
|
http_parser: ^4.0.0
|
||||||
image_gallery_saver: ^1.7.1
|
image_gallery_saver: ^2.0.1
|
||||||
image_picker: ^0.8.2
|
image_picker: ^0.8.2
|
||||||
jiffy: ^5.0.0
|
jiffy: ^5.0.0
|
||||||
lottie: ^2.0.0
|
lottie: ^2.0.0
|
||||||
@@ -37,8 +37,8 @@ dependencies:
|
|||||||
photo_view: ^0.14.0
|
photo_view: ^0.14.0
|
||||||
rxdart: ^0.27.0
|
rxdart: ^0.27.0
|
||||||
share_plus: ^6.3.0
|
share_plus: ^6.3.0
|
||||||
shimmer: ^2.0.0
|
shimmer: ^3.0.0
|
||||||
stream_chat_flutter_core: ^6.1.0
|
stream_chat_flutter_core: ^6.2.0
|
||||||
synchronized: ^3.0.0
|
synchronized: ^3.0.0
|
||||||
thumblr: ^0.0.4
|
thumblr: ^0.0.4
|
||||||
url_launcher: ^6.1.0
|
url_launcher: ^6.1.0
|
||||||
|
|||||||
+2
-3
@@ -22,9 +22,8 @@ void main() {
|
|||||||
EdgeInsets? padding,
|
EdgeInsets? padding,
|
||||||
int initialScrollIndex = 0,
|
int initialScrollIndex = 0,
|
||||||
}) async {
|
}) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
|||||||
+2
-3
@@ -24,9 +24,8 @@ void main() {
|
|||||||
double anchor = 0,
|
double anchor = 0,
|
||||||
int itemCount = defaultItemCount,
|
int itemCount = defaultItemCount,
|
||||||
}) async {
|
}) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
|||||||
+2
-3
@@ -23,9 +23,8 @@ void main() {
|
|||||||
double anchor = 0,
|
double anchor = 0,
|
||||||
int itemCount = defaultItemCount,
|
int itemCount = defaultItemCount,
|
||||||
}) async {
|
}) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
|||||||
+2
-3
@@ -21,9 +21,8 @@ void main() {
|
|||||||
EdgeInsets? padding,
|
EdgeInsets? padding,
|
||||||
int initialIndex = 0,
|
int initialIndex = 0,
|
||||||
}) async {
|
}) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
|||||||
+30
-45
@@ -37,9 +37,8 @@ void main() {
|
|||||||
double? minCacheExtent,
|
double? minCacheExtent,
|
||||||
bool variableHeight = false,
|
bool variableHeight = false,
|
||||||
}) async {
|
}) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -105,9 +104,8 @@ void main() {
|
|||||||
testWidgets('List positioned with 0 at top - use default values',
|
testWidgets('List positioned with 0 at top - use default values',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
final itemPositionsListener = ItemPositionsListener.create();
|
final itemPositionsListener = ItemPositionsListener.create();
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -1543,9 +1541,8 @@ void main() {
|
|||||||
final itemPositionsListener = ItemPositionsListener.create();
|
final itemPositionsListener = ItemPositionsListener.create();
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
|
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -1600,9 +1597,8 @@ void main() {
|
|||||||
final itemPositionsListener = ItemPositionsListener.create();
|
final itemPositionsListener = ItemPositionsListener.create();
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
|
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -1661,9 +1657,8 @@ void main() {
|
|||||||
final itemPositionsListener = ItemPositionsListener.create();
|
final itemPositionsListener = ItemPositionsListener.create();
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
|
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -1732,9 +1727,8 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('Jump to 100 then set itemCount to 0',
|
testWidgets('Jump to 100 then set itemCount to 0',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
final itemPositionsListener = ItemPositionsListener.create();
|
final itemPositionsListener = ItemPositionsListener.create();
|
||||||
@@ -1781,9 +1775,8 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('List positioned with 100 at top then set itemCount to 100',
|
testWidgets('List positioned with 100 at top then set itemCount to 100',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
final itemCount = ValueNotifier<int>(defaultItemCount);
|
final itemCount = ValueNotifier<int>(defaultItemCount);
|
||||||
|
|
||||||
@@ -1823,9 +1816,8 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('List positioned with 499 at bottom then set itemCount to 100',
|
testWidgets('List positioned with 499 at bottom then set itemCount to 100',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
final itemCount = ValueNotifier<int>(defaultItemCount);
|
final itemCount = ValueNotifier<int>(defaultItemCount);
|
||||||
|
|
||||||
@@ -1943,9 +1935,8 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Rebuild with scroll controller', (WidgetTester tester) async {
|
testWidgets('Rebuild with scroll controller', (WidgetTester tester) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
final key = ValueNotifier<Key>(const ValueKey('key'));
|
final key = ValueNotifier<Key>(const ValueKey('key'));
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
|
|
||||||
@@ -1986,9 +1977,8 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('Double rebuild with scroll controller',
|
testWidgets('Double rebuild with scroll controller',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
final outerKey = ValueNotifier<Key>(const ValueKey('outerKey'));
|
final outerKey = ValueNotifier<Key>(const ValueKey('outerKey'));
|
||||||
final innerKey = GlobalKey();
|
final innerKey = GlobalKey();
|
||||||
final listKey = ValueNotifier<Key>(const ValueKey(null));
|
final listKey = ValueNotifier<Key>(const ValueKey(null));
|
||||||
@@ -2036,9 +2026,8 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Key change with scroll controller', (WidgetTester tester) async {
|
testWidgets('Key change with scroll controller', (WidgetTester tester) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
final key = ValueNotifier<Key>(const ValueKey('key'));
|
final key = ValueNotifier<Key>(const ValueKey('key'));
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
|
|
||||||
@@ -2070,9 +2059,8 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Scroll after rebuild', (WidgetTester tester) async {
|
testWidgets('Scroll after rebuild', (WidgetTester tester) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
final key = ValueNotifier<Key>(const ValueKey('key'));
|
final key = ValueNotifier<Key>(const ValueKey('key'));
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
|
|
||||||
@@ -2113,9 +2101,8 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('Scroll after rebuild when resusing state',
|
testWidgets('Scroll after rebuild when resusing state',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
final containerKey = ValueNotifier<Key>(const ValueKey('key'));
|
final containerKey = ValueNotifier<Key>(const ValueKey('key'));
|
||||||
final scrollKey = GlobalKey();
|
final scrollKey = GlobalKey();
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
@@ -2158,9 +2145,8 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('Scroll after changing scroll controller',
|
testWidgets('Scroll after changing scroll controller',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
final itemScrollController0 = ItemScrollController();
|
final itemScrollController0 = ItemScrollController();
|
||||||
final itemScrollController1 = ItemScrollController();
|
final itemScrollController1 = ItemScrollController();
|
||||||
@@ -2205,9 +2191,8 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('Scroll after swapping scroll controllers',
|
testWidgets('Scroll after swapping scroll controllers',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
final itemScrollController0 = ItemScrollController();
|
final itemScrollController0 = ItemScrollController();
|
||||||
final itemScrollController1 = ItemScrollController();
|
final itemScrollController1 = ItemScrollController();
|
||||||
|
|||||||
+2
-3
@@ -25,9 +25,8 @@ void main() {
|
|||||||
double anchor = 0,
|
double anchor = 0,
|
||||||
int itemCount = defaultItemCount,
|
int itemCount = defaultItemCount,
|
||||||
}) async {
|
}) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
|||||||
+8
-12
@@ -33,9 +33,8 @@ void main() {
|
|||||||
bool addRepaintBoundaries = true,
|
bool addRepaintBoundaries = true,
|
||||||
bool addAutomaticKeepAlives = true,
|
bool addAutomaticKeepAlives = true,
|
||||||
}) async {
|
}) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -100,9 +99,8 @@ void main() {
|
|||||||
testWidgets('List positioned with 0 at top - use default values',
|
testWidgets('List positioned with 0 at top - use default values',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
final itemPositionsListener = ItemPositionsListener.create();
|
final itemPositionsListener = ItemPositionsListener.create();
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -484,9 +482,8 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('Empty list then update to single item list',
|
testWidgets('Empty list then update to single item list',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
final itemPositionsListener = ItemPositionsListener.create();
|
final itemPositionsListener = ItemPositionsListener.create();
|
||||||
@@ -526,9 +523,8 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('ItemPositions: Empty list then update to 10 items list',
|
testWidgets('ItemPositions: Empty list then update to 10 items list',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
final itemScrollController = ItemScrollController();
|
final itemScrollController = ItemScrollController();
|
||||||
final itemPositionsListener = ItemPositionsListener.create();
|
final itemPositionsListener = ItemPositionsListener.create();
|
||||||
|
|||||||
+2
-3
@@ -24,9 +24,8 @@ void main() {
|
|||||||
EdgeInsets? padding,
|
EdgeInsets? padding,
|
||||||
int initialScrollIndex = 0,
|
int initialScrollIndex = 0,
|
||||||
}) async {
|
}) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
|||||||
+4
-6
@@ -25,9 +25,8 @@ void main() {
|
|||||||
int itemCount = defaultItemCount,
|
int itemCount = defaultItemCount,
|
||||||
bool reverse = false,
|
bool reverse = false,
|
||||||
}) async {
|
}) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -421,9 +420,8 @@ void main() {
|
|||||||
testWidgets('test nested positioned list', (WidgetTester tester) async {
|
testWidgets('test nested positioned list', (WidgetTester tester) async {
|
||||||
const itemCount = 50;
|
const itemCount = 50;
|
||||||
const key = Key('short_list');
|
const key = Key('short_list');
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
|||||||
+2
-3
@@ -21,9 +21,8 @@ void main() {
|
|||||||
EdgeInsets? padding,
|
EdgeInsets? padding,
|
||||||
int initialIndex = 0,
|
int initialIndex = 0,
|
||||||
}) async {
|
}) async {
|
||||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
tester.view.devicePixelRatio = 1.0;
|
||||||
tester.binding.window.physicalSizeTestValue =
|
tester.view.physicalSize = const Size(screenWidth, screenHeight);
|
||||||
const Size(screenWidth, screenHeight);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
|||||||
@@ -39,20 +39,27 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
methodChannel.setMockMethodCallHandler((MethodCall methodCall) async {
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
if (methodCall.method == 'listen') {
|
.setMockMethodCallHandler(
|
||||||
try {
|
methodChannel,
|
||||||
await ServicesBinding.instance.defaultBinaryMessenger
|
(MethodCall methodCall) async {
|
||||||
.handlePlatformMessage(
|
if (methodCall.method == 'listen') {
|
||||||
methodChannel.name,
|
try {
|
||||||
methodChannel.codec.encodeSuccessEnvelope('wifi'),
|
await TestDefaultBinaryMessengerBinding
|
||||||
(_) {},
|
.instance.defaultBinaryMessenger
|
||||||
);
|
.handlePlatformMessage(
|
||||||
} catch (e) {
|
methodChannel.name,
|
||||||
print(e);
|
methodChannel.codec.encodeSuccessEnvelope('wifi'),
|
||||||
|
(_) {},
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
return null;
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
@@ -118,7 +125,8 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
tearDown(() {
|
tearDown(() {
|
||||||
methodChannel.setMockMethodCallHandler(null);
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
|
.setMockMethodCallHandler(methodChannel, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
/*testGoldens(
|
/*testGoldens(
|
||||||
|
|||||||
@@ -11,10 +11,13 @@ void main() {
|
|||||||
const methodChannel =
|
const methodChannel =
|
||||||
MethodChannel('dev.fluttercommunity.plus/connectivity_status');
|
MethodChannel('dev.fluttercommunity.plus/connectivity_status');
|
||||||
setUp(() {
|
setUp(() {
|
||||||
methodChannel.setMockMethodCallHandler((MethodCall methodCall) async {
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
|
.setMockMethodCallHandler(methodChannel,
|
||||||
|
(MethodCall methodCall) async {
|
||||||
if (methodCall.method == 'listen') {
|
if (methodCall.method == 'listen') {
|
||||||
try {
|
try {
|
||||||
await ServicesBinding.instance.defaultBinaryMessenger
|
await TestDefaultBinaryMessengerBinding
|
||||||
|
.instance.defaultBinaryMessenger
|
||||||
.handlePlatformMessage(
|
.handlePlatformMessage(
|
||||||
methodChannel.name,
|
methodChannel.name,
|
||||||
methodChannel.codec.encodeSuccessEnvelope('wifi'),
|
methodChannel.codec.encodeSuccessEnvelope('wifi'),
|
||||||
@@ -24,6 +27,7 @@ void main() {
|
|||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -87,7 +91,8 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
tearDown(() {
|
tearDown(() {
|
||||||
methodChannel.setMockMethodCallHandler(null);
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
|
.setMockMethodCallHandler(methodChannel, null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,13 @@ void main() {
|
|||||||
const methodChannel =
|
const methodChannel =
|
||||||
MethodChannel('dev.fluttercommunity.plus/connectivity_status');
|
MethodChannel('dev.fluttercommunity.plus/connectivity_status');
|
||||||
setUp(() {
|
setUp(() {
|
||||||
methodChannel.setMockMethodCallHandler((MethodCall methodCall) async {
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
|
.setMockMethodCallHandler(methodChannel,
|
||||||
|
(MethodCall methodCall) async {
|
||||||
if (methodCall.method == 'listen') {
|
if (methodCall.method == 'listen') {
|
||||||
try {
|
try {
|
||||||
await ServicesBinding.instance.defaultBinaryMessenger
|
await TestDefaultBinaryMessengerBinding
|
||||||
|
.instance.defaultBinaryMessenger
|
||||||
.handlePlatformMessage(
|
.handlePlatformMessage(
|
||||||
methodChannel.name,
|
methodChannel.name,
|
||||||
methodChannel.codec.encodeSuccessEnvelope('wifi'),
|
methodChannel.codec.encodeSuccessEnvelope('wifi'),
|
||||||
@@ -24,6 +27,7 @@ void main() {
|
|||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -93,7 +97,8 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
tearDown(() {
|
tearDown(() {
|
||||||
methodChannel.setMockMethodCallHandler(null);
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
|
.setMockMethodCallHandler(methodChannel, null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,10 +40,12 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
methodChannel.setMockMethodCallHandler((MethodCall methodCall) async {
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
|
.setMockMethodCallHandler(methodChannel, (MethodCall methodCall) async {
|
||||||
if (methodCall.method == 'listen') {
|
if (methodCall.method == 'listen') {
|
||||||
try {
|
try {
|
||||||
await ServicesBinding.instance.defaultBinaryMessenger
|
await TestDefaultBinaryMessengerBinding
|
||||||
|
.instance.defaultBinaryMessenger
|
||||||
.handlePlatformMessage(
|
.handlePlatformMessage(
|
||||||
methodChannel.name,
|
methodChannel.name,
|
||||||
methodChannel.codec.encodeSuccessEnvelope('wifi'),
|
methodChannel.codec.encodeSuccessEnvelope('wifi'),
|
||||||
@@ -53,6 +55,7 @@ void main() {
|
|||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -106,6 +109,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tearDown(() {
|
tearDown(() {
|
||||||
methodChannel.setMockMethodCallHandler(null);
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
|
.setMockMethodCallHandler(methodChannel, null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,10 +40,12 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
methodChannel.setMockMethodCallHandler((MethodCall methodCall) async {
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
|
.setMockMethodCallHandler(methodChannel, (MethodCall methodCall) async {
|
||||||
if (methodCall.method == 'listen') {
|
if (methodCall.method == 'listen') {
|
||||||
try {
|
try {
|
||||||
await ServicesBinding.instance.defaultBinaryMessenger
|
await TestDefaultBinaryMessengerBinding
|
||||||
|
.instance.defaultBinaryMessenger
|
||||||
.handlePlatformMessage(
|
.handlePlatformMessage(
|
||||||
methodChannel.name,
|
methodChannel.name,
|
||||||
methodChannel.codec.encodeSuccessEnvelope('wifi'),
|
methodChannel.codec.encodeSuccessEnvelope('wifi'),
|
||||||
@@ -53,6 +55,7 @@ void main() {
|
|||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -110,6 +113,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tearDown(() {
|
tearDown(() {
|
||||||
methodChannel.setMockMethodCallHandler(null);
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
|
.setMockMethodCallHandler(methodChannel, null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,11 +69,11 @@ final _messageInputThemeControlMidLerp = StreamMessageInputThemeData(
|
|||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
sendAnimationDuration: const Duration(milliseconds: 300),
|
sendAnimationDuration: const Duration(milliseconds: 300),
|
||||||
inputBackgroundColor: const Color(0xff87898b),
|
inputBackgroundColor: const Color(0xff87898b),
|
||||||
actionButtonColor: const Color(0xff005fff),
|
actionButtonColor: const Color(0xff196eff),
|
||||||
actionButtonIdleColor: const Color(0xff7a7a7a),
|
actionButtonIdleColor: const Color(0xff7a7a7a),
|
||||||
sendButtonColor: const Color(0xff005fff),
|
sendButtonColor: const Color(0xff196eff),
|
||||||
sendButtonIdleColor: const Color(0xff848585),
|
sendButtonIdleColor: const Color(0xff848585),
|
||||||
expandButtonColor: const Color(0xff005fff),
|
expandButtonColor: const Color(0xff196eff),
|
||||||
inputTextStyle: const TextStyle(
|
inputTextStyle: const TextStyle(
|
||||||
color: Color(0xff7f7f7f),
|
color: Color(0xff7f7f7f),
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
|
## 6.2.0
|
||||||
|
|
||||||
|
- Fixed `StreamMessageInputController.textPatternStyle` not matching case-insensitive patterns.
|
||||||
|
- Updated `connectivity_plus` dependency to `^4.0.0`
|
||||||
|
- Fixed `StreamChannel` shows black screen while loading in some cases.
|
||||||
|
- Updated `stream_chat` dependency to [`6.2.0`](https://pub.dev/packages/stream_chat/changelog).
|
||||||
|
|
||||||
## 6.1.0
|
## 6.1.0
|
||||||
|
|
||||||
- Updated `dart` sdk environment range to support `3.0.0`.
|
- Updated `dart` sdk environment range to support `3.0.0`.
|
||||||
- Updated `stream_chat` dependency to [`6.1.0`](https://pub.dev/packages/stream_chat/changelog).
|
- Updated `stream_chat` dependency to [`6.1.0`](https://pub.dev/packages/stream_chat/changelog).
|
||||||
- [[#1356]](https://github.com/GetStream/stream-chat-flutter/issues/1356) Channel doesn't auto display again after being
|
- [[#1356]](https://github.com/GetStream/stream-chat-flutter/issues/1356) Channel doesn't auto display again after being
|
||||||
hidden.
|
hidden.
|
||||||
|
- [[#1540]](https://github.com/GetStream/stream-chat-flutter/issues/1540) Use `CircularProgressIndicator.adaptive`
|
||||||
|
instead of material indicator.
|
||||||
|
|
||||||
## 6.0.0
|
## 6.0.0
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ bool Function(Message) defaultMessageFilter(String currentUserId) =>
|
|||||||
/// },
|
/// },
|
||||||
/// loadingBuilder: (context) {
|
/// loadingBuilder: (context) {
|
||||||
/// return Center(
|
/// return Center(
|
||||||
/// child: CircularProgressIndicator(),
|
/// child: CircularProgressIndicator.adaptive(),
|
||||||
/// );
|
/// );
|
||||||
/// },
|
/// },
|
||||||
/// messageListBuilder: (context, list) {
|
/// messageListBuilder: (context, list) {
|
||||||
|
|||||||
@@ -41,7 +41,10 @@ class MessageTextFieldController extends TextEditingController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return TextSpan(text: text, style: style).splitMapJoin(
|
return TextSpan(text: text, style: style).splitMapJoin(
|
||||||
RegExp(pattern.keys.map((it) => it.pattern).join('|')),
|
RegExp(
|
||||||
|
pattern.keys.map((it) => it.pattern).join('|'),
|
||||||
|
caseSensitive: false,
|
||||||
|
),
|
||||||
onMatch: (match) {
|
onMatch: (match) {
|
||||||
final text = match[0]!;
|
final text = match[0]!;
|
||||||
final key = pattern.keys.firstWhere((it) => it.hasMatch(text));
|
final key = pattern.keys.firstWhere((it) => it.hasMatch(text));
|
||||||
|
|||||||
@@ -251,14 +251,14 @@ class StreamChannelState extends State<StreamChannel> {
|
|||||||
channel.state!.truncate();
|
channel.state!.truncate();
|
||||||
|
|
||||||
if (messageId == null) {
|
if (messageId == null) {
|
||||||
await channel.query(
|
final state = await channel.query(
|
||||||
messagesPagination: PaginationParams(
|
messagesPagination: PaginationParams(
|
||||||
limit: limit,
|
limit: limit,
|
||||||
),
|
),
|
||||||
preferOffline: preferOffline,
|
preferOffline: preferOffline,
|
||||||
);
|
);
|
||||||
channel.state!.isUpToDate = true;
|
channel.state!.isUpToDate = true;
|
||||||
return null;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
return channel.query(
|
return channel.query(
|
||||||
@@ -445,13 +445,13 @@ class StreamChannelState extends State<StreamChannel> {
|
|||||||
final dataLoaded = snapshot.data?.every((it) => it) == true;
|
final dataLoaded = snapshot.data?.every((it) => it) == true;
|
||||||
if (widget.showLoading && !dataLoaded) {
|
if (widget.showLoading && !dataLoaded) {
|
||||||
return const Center(
|
return const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator.adaptive(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return widget.child;
|
return widget.child;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (initialMessageId != null) {
|
if (_futures.length > 1) {
|
||||||
child = Material(child: child);
|
child = Material(child: child);
|
||||||
}
|
}
|
||||||
return child;
|
return 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: 6.1.0
|
version: 6.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
|
||||||
|
|
||||||
@@ -11,13 +11,13 @@ environment:
|
|||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
collection: ^1.17.0
|
collection: ^1.17.0
|
||||||
connectivity_plus: ^3.0.2
|
connectivity_plus: ^4.0.0
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
freezed_annotation: ^2.0.3
|
freezed_annotation: ^2.0.3
|
||||||
meta: ^1.8.0
|
meta: ^1.8.0
|
||||||
rxdart: ^0.27.0
|
rxdart: ^0.27.0
|
||||||
stream_chat: ^6.1.0
|
stream_chat: ^6.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.3.3
|
build_runner: ^2.3.3
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
## 5.2.0
|
||||||
|
|
||||||
|
* Updated `stream_chat_flutter` dependency to [`6.2.0`](https://pub.dev/packages/stream_chat_flutter/changelog).
|
||||||
|
|
||||||
## 5.1.0
|
## 5.1.0
|
||||||
|
|
||||||
* Updated `dart` sdk environment range to support `3.0.0`.
|
* Updated `dart` sdk environment range to support `3.0.0`.
|
||||||
|
|||||||
@@ -577,10 +577,10 @@ class ChannelPage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return const Scaffold(
|
||||||
appBar: const StreamChannelHeader(),
|
appBar: StreamChannelHeader(),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: const <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: StreamMessageListView(),
|
child: StreamMessageListView(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -106,10 +106,10 @@ class ChannelPage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return const Scaffold(
|
||||||
appBar: const StreamChannelHeader(),
|
appBar: StreamChannelHeader(),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: const <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: StreamMessageListView(),
|
child: StreamMessageListView(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -133,10 +133,10 @@ class ChannelPage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return const Scaffold(
|
||||||
appBar: const StreamChannelHeader(),
|
appBar: StreamChannelHeader(),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: const <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: StreamMessageListView(),
|
child: StreamMessageListView(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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: 5.1.0
|
version: 5.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,7 +14,7 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
stream_chat_flutter: ^6.1.0
|
stream_chat_flutter: ^6.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
dart_code_metrics: ^5.7.2
|
dart_code_metrics: ^5.7.2
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
## 6.2.0
|
||||||
|
|
||||||
|
- Added support for `StreamChatPersistenceClient.isConnected` for checking if the client is connected to the database.
|
||||||
|
- [[#1422]](https://github.com/GetStream/stream-chat-flutter/issues/1422) Removed default values
|
||||||
|
from `UserEntity` `createdAt` and `updatedAt` fields.
|
||||||
|
- Updated `stream_chat` dependency to [`6.2.0`](https://pub.dev/packages/stream_chat/changelog).
|
||||||
|
|
||||||
## 6.1.0
|
## 6.1.0
|
||||||
|
|
||||||
- Updated `dart` sdk environment range to support `3.0.0`.
|
- Updated `dart` sdk environment range to support `3.0.0`.
|
||||||
|
|||||||
@@ -81,13 +81,13 @@ class ChannelQueryDao extends DatabaseAccessor<DriftChatDatabase>
|
|||||||
final cachedChannelCids = await getCachedChannelCids(filter);
|
final cachedChannelCids = await getCachedChannelCids(filter);
|
||||||
final query = select(channels)..where((c) => c.cid.isIn(cachedChannelCids));
|
final query = select(channels)..where((c) => c.cid.isIn(cachedChannelCids));
|
||||||
|
|
||||||
final cachedChannels = await (query.join([
|
final cachedChannels = await query.join([
|
||||||
leftOuterJoin(users, channels.createdById.equalsExp(users.id)),
|
leftOuterJoin(users, channels.createdById.equalsExp(users.id)),
|
||||||
]).map((row) {
|
]).map((row) {
|
||||||
final createdByEntity = row.readTableOrNull(users);
|
final createdByEntity = row.readTableOrNull(users);
|
||||||
final channelEntity = row.readTable(channels);
|
final channelEntity = row.readTable(channels);
|
||||||
return channelEntity.toChannelModel(createdBy: createdByEntity?.toUser());
|
return channelEntity.toChannelModel(createdBy: createdByEntity?.toUser());
|
||||||
})).get();
|
}).get();
|
||||||
|
|
||||||
var chainedComparator = (ChannelModel a, ChannelModel b) {
|
var chainedComparator = (ChannelModel a, ChannelModel b) {
|
||||||
final dateA = a.lastMessageAt ?? a.createdAt;
|
final dateA = a.lastMessageAt ?? a.createdAt;
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
import 'package:stream_chat_persistence/src/db/drift_chat_database.dart';
|
import 'package:stream_chat_persistence/src/db/drift_chat_database.dart';
|
||||||
|
|
||||||
import 'package:stream_chat_persistence/src/entity/members.dart';
|
import 'package:stream_chat_persistence/src/entity/members.dart';
|
||||||
import 'package:stream_chat_persistence/src/entity/users.dart';
|
import 'package:stream_chat_persistence/src/entity/users.dart';
|
||||||
|
|
||||||
import 'package:stream_chat_persistence/src/mapper/mapper.dart';
|
import 'package:stream_chat_persistence/src/mapper/mapper.dart';
|
||||||
|
|
||||||
part 'member_dao.g.dart';
|
part 'member_dao.g.dart';
|
||||||
@@ -39,9 +37,9 @@ class MemberDao extends DatabaseAccessor<DriftChatDatabase>
|
|||||||
) {
|
) {
|
||||||
final entities = channelWithMembers.entries
|
final entities = channelWithMembers.entries
|
||||||
.map((entry) =>
|
.map((entry) =>
|
||||||
(entry.value?.map(
|
entry.value?.map(
|
||||||
(member) => member.toEntity(cid: entry.key),
|
(member) => member.toEntity(cid: entry.key),
|
||||||
)) ??
|
) ??
|
||||||
[])
|
[])
|
||||||
.expand((it) => it)
|
.expand((it) => it)
|
||||||
.toList(growable: false);
|
.toList(growable: false);
|
||||||
|
|||||||
@@ -49,7 +49,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 => 10;
|
int get schemaVersion => 11;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration => MigrationStrategy(
|
MigrationStrategy get migration => MigrationStrategy(
|
||||||
|
|||||||
@@ -3651,18 +3651,14 @@ class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> {
|
|||||||
const VerificationMeta('createdAt');
|
const VerificationMeta('createdAt');
|
||||||
@override
|
@override
|
||||||
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
|
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
|
||||||
'created_at', aliasedName, false,
|
'created_at', aliasedName, true,
|
||||||
type: DriftSqlType.dateTime,
|
type: DriftSqlType.dateTime, requiredDuringInsert: false);
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultValue: currentDateAndTime);
|
|
||||||
static const VerificationMeta _updatedAtMeta =
|
static const VerificationMeta _updatedAtMeta =
|
||||||
const VerificationMeta('updatedAt');
|
const VerificationMeta('updatedAt');
|
||||||
@override
|
@override
|
||||||
late final GeneratedColumn<DateTime> updatedAt = GeneratedColumn<DateTime>(
|
late final GeneratedColumn<DateTime> updatedAt = GeneratedColumn<DateTime>(
|
||||||
'updated_at', aliasedName, false,
|
'updated_at', aliasedName, true,
|
||||||
type: DriftSqlType.dateTime,
|
type: DriftSqlType.dateTime, requiredDuringInsert: false);
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultValue: currentDateAndTime);
|
|
||||||
static const VerificationMeta _lastActiveMeta =
|
static const VerificationMeta _lastActiveMeta =
|
||||||
const VerificationMeta('lastActive');
|
const VerificationMeta('lastActive');
|
||||||
@override
|
@override
|
||||||
@@ -3773,9 +3769,9 @@ class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> {
|
|||||||
language: attachedDatabase.typeMapping
|
language: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.string, data['${effectivePrefix}language']),
|
.read(DriftSqlType.string, data['${effectivePrefix}language']),
|
||||||
createdAt: attachedDatabase.typeMapping
|
createdAt: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
|
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at']),
|
||||||
updatedAt: attachedDatabase.typeMapping
|
updatedAt: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
|
.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at']),
|
||||||
lastActive: attachedDatabase.typeMapping
|
lastActive: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.dateTime, data['${effectivePrefix}last_active']),
|
.read(DriftSqlType.dateTime, data['${effectivePrefix}last_active']),
|
||||||
online: attachedDatabase.typeMapping
|
online: attachedDatabase.typeMapping
|
||||||
@@ -3808,10 +3804,10 @@ class UserEntity extends DataClass implements Insertable<UserEntity> {
|
|||||||
final String? language;
|
final String? language;
|
||||||
|
|
||||||
/// Date of user creation
|
/// Date of user creation
|
||||||
final DateTime createdAt;
|
final DateTime? createdAt;
|
||||||
|
|
||||||
/// Date of last user update
|
/// Date of last user update
|
||||||
final DateTime updatedAt;
|
final DateTime? updatedAt;
|
||||||
|
|
||||||
/// Date of last user connection
|
/// Date of last user connection
|
||||||
final DateTime? lastActive;
|
final DateTime? lastActive;
|
||||||
@@ -3828,8 +3824,8 @@ class UserEntity extends DataClass implements Insertable<UserEntity> {
|
|||||||
{required this.id,
|
{required this.id,
|
||||||
this.role,
|
this.role,
|
||||||
this.language,
|
this.language,
|
||||||
required this.createdAt,
|
this.createdAt,
|
||||||
required this.updatedAt,
|
this.updatedAt,
|
||||||
this.lastActive,
|
this.lastActive,
|
||||||
required this.online,
|
required this.online,
|
||||||
required this.banned,
|
required this.banned,
|
||||||
@@ -3844,8 +3840,12 @@ class UserEntity extends DataClass implements Insertable<UserEntity> {
|
|||||||
if (!nullToAbsent || language != null) {
|
if (!nullToAbsent || language != null) {
|
||||||
map['language'] = Variable<String>(language);
|
map['language'] = Variable<String>(language);
|
||||||
}
|
}
|
||||||
map['created_at'] = Variable<DateTime>(createdAt);
|
if (!nullToAbsent || createdAt != null) {
|
||||||
map['updated_at'] = Variable<DateTime>(updatedAt);
|
map['created_at'] = Variable<DateTime>(createdAt);
|
||||||
|
}
|
||||||
|
if (!nullToAbsent || updatedAt != null) {
|
||||||
|
map['updated_at'] = Variable<DateTime>(updatedAt);
|
||||||
|
}
|
||||||
if (!nullToAbsent || lastActive != null) {
|
if (!nullToAbsent || lastActive != null) {
|
||||||
map['last_active'] = Variable<DateTime>(lastActive);
|
map['last_active'] = Variable<DateTime>(lastActive);
|
||||||
}
|
}
|
||||||
@@ -3865,8 +3865,8 @@ class UserEntity extends DataClass implements Insertable<UserEntity> {
|
|||||||
id: serializer.fromJson<String>(json['id']),
|
id: serializer.fromJson<String>(json['id']),
|
||||||
role: serializer.fromJson<String?>(json['role']),
|
role: serializer.fromJson<String?>(json['role']),
|
||||||
language: serializer.fromJson<String?>(json['language']),
|
language: serializer.fromJson<String?>(json['language']),
|
||||||
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
|
createdAt: serializer.fromJson<DateTime?>(json['createdAt']),
|
||||||
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
|
updatedAt: serializer.fromJson<DateTime?>(json['updatedAt']),
|
||||||
lastActive: serializer.fromJson<DateTime?>(json['lastActive']),
|
lastActive: serializer.fromJson<DateTime?>(json['lastActive']),
|
||||||
online: serializer.fromJson<bool>(json['online']),
|
online: serializer.fromJson<bool>(json['online']),
|
||||||
banned: serializer.fromJson<bool>(json['banned']),
|
banned: serializer.fromJson<bool>(json['banned']),
|
||||||
@@ -3880,8 +3880,8 @@ class UserEntity extends DataClass implements Insertable<UserEntity> {
|
|||||||
'id': serializer.toJson<String>(id),
|
'id': serializer.toJson<String>(id),
|
||||||
'role': serializer.toJson<String?>(role),
|
'role': serializer.toJson<String?>(role),
|
||||||
'language': serializer.toJson<String?>(language),
|
'language': serializer.toJson<String?>(language),
|
||||||
'createdAt': serializer.toJson<DateTime>(createdAt),
|
'createdAt': serializer.toJson<DateTime?>(createdAt),
|
||||||
'updatedAt': serializer.toJson<DateTime>(updatedAt),
|
'updatedAt': serializer.toJson<DateTime?>(updatedAt),
|
||||||
'lastActive': serializer.toJson<DateTime?>(lastActive),
|
'lastActive': serializer.toJson<DateTime?>(lastActive),
|
||||||
'online': serializer.toJson<bool>(online),
|
'online': serializer.toJson<bool>(online),
|
||||||
'banned': serializer.toJson<bool>(banned),
|
'banned': serializer.toJson<bool>(banned),
|
||||||
@@ -3893,8 +3893,8 @@ class UserEntity extends DataClass implements Insertable<UserEntity> {
|
|||||||
{String? id,
|
{String? id,
|
||||||
Value<String?> role = const Value.absent(),
|
Value<String?> role = const Value.absent(),
|
||||||
Value<String?> language = const Value.absent(),
|
Value<String?> language = const Value.absent(),
|
||||||
DateTime? createdAt,
|
Value<DateTime?> createdAt = const Value.absent(),
|
||||||
DateTime? updatedAt,
|
Value<DateTime?> updatedAt = const Value.absent(),
|
||||||
Value<DateTime?> lastActive = const Value.absent(),
|
Value<DateTime?> lastActive = const Value.absent(),
|
||||||
bool? online,
|
bool? online,
|
||||||
bool? banned,
|
bool? banned,
|
||||||
@@ -3903,8 +3903,8 @@ class UserEntity extends DataClass implements Insertable<UserEntity> {
|
|||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
role: role.present ? role.value : this.role,
|
role: role.present ? role.value : this.role,
|
||||||
language: language.present ? language.value : this.language,
|
language: language.present ? language.value : this.language,
|
||||||
createdAt: createdAt ?? this.createdAt,
|
createdAt: createdAt.present ? createdAt.value : this.createdAt,
|
||||||
updatedAt: updatedAt ?? this.updatedAt,
|
updatedAt: updatedAt.present ? updatedAt.value : this.updatedAt,
|
||||||
lastActive: lastActive.present ? lastActive.value : this.lastActive,
|
lastActive: lastActive.present ? lastActive.value : this.lastActive,
|
||||||
online: online ?? this.online,
|
online: online ?? this.online,
|
||||||
banned: banned ?? this.banned,
|
banned: banned ?? this.banned,
|
||||||
@@ -3948,8 +3948,8 @@ class UsersCompanion extends UpdateCompanion<UserEntity> {
|
|||||||
final Value<String> id;
|
final Value<String> id;
|
||||||
final Value<String?> role;
|
final Value<String?> role;
|
||||||
final Value<String?> language;
|
final Value<String?> language;
|
||||||
final Value<DateTime> createdAt;
|
final Value<DateTime?> createdAt;
|
||||||
final Value<DateTime> updatedAt;
|
final Value<DateTime?> updatedAt;
|
||||||
final Value<DateTime?> lastActive;
|
final Value<DateTime?> lastActive;
|
||||||
final Value<bool> online;
|
final Value<bool> online;
|
||||||
final Value<bool> banned;
|
final Value<bool> banned;
|
||||||
@@ -4010,8 +4010,8 @@ class UsersCompanion extends UpdateCompanion<UserEntity> {
|
|||||||
{Value<String>? id,
|
{Value<String>? id,
|
||||||
Value<String?>? role,
|
Value<String?>? role,
|
||||||
Value<String?>? language,
|
Value<String?>? language,
|
||||||
Value<DateTime>? createdAt,
|
Value<DateTime?>? createdAt,
|
||||||
Value<DateTime>? updatedAt,
|
Value<DateTime?>? updatedAt,
|
||||||
Value<DateTime?>? lastActive,
|
Value<DateTime?>? lastActive,
|
||||||
Value<bool>? online,
|
Value<bool>? online,
|
||||||
Value<bool>? banned,
|
Value<bool>? banned,
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ class Users extends Table {
|
|||||||
TextColumn get language => text().nullable()();
|
TextColumn get language => text().nullable()();
|
||||||
|
|
||||||
/// Date of user creation
|
/// Date of user creation
|
||||||
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
DateTimeColumn get createdAt => dateTime().nullable()();
|
||||||
|
|
||||||
/// Date of last user update
|
/// Date of last user update
|
||||||
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
|
DateTimeColumn get updatedAt => dateTime().nullable()();
|
||||||
|
|
||||||
/// Date of last user connection
|
/// Date of last user connection
|
||||||
DateTimeColumn get lastActive => dateTime().nullable()();
|
DateTimeColumn get lastActive => dateTime().nullable()();
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class StreamChatPersistenceClient extends ChatPersistenceClient {
|
|||||||
|
|
||||||
bool get _debugIsConnected {
|
bool get _debugIsConnected {
|
||||||
assert(() {
|
assert(() {
|
||||||
if (db == null) {
|
if (!isConnected) {
|
||||||
throw StateError('''
|
throw StateError('''
|
||||||
$runtimeType hasn't been connected yet or used after `disconnect`
|
$runtimeType hasn't been connected yet or used after `disconnect`
|
||||||
was called. Consider calling `connect` to create a connection.
|
was called. Consider calling `connect` to create a connection.
|
||||||
@@ -79,6 +79,9 @@ class StreamChatPersistenceClient extends ChatPersistenceClient {
|
|||||||
webUseIndexedDbIfSupported: _webUseIndexedDbIfSupported,
|
webUseIndexedDbIfSupported: _webUseIndexedDbIfSupported,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isConnected => db != null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> connect(
|
Future<void> connect(
|
||||||
String userId, {
|
String userId, {
|
||||||
@@ -405,7 +408,7 @@ class StreamChatPersistenceClient extends ChatPersistenceClient {
|
|||||||
@override
|
@override
|
||||||
Future<void> disconnect({bool flush = false}) async {
|
Future<void> disconnect({bool flush = false}) async {
|
||||||
_logger.info('disconnect');
|
_logger.info('disconnect');
|
||||||
if (db != null) {
|
if (isConnected) {
|
||||||
_logger.info('Disconnecting');
|
_logger.info('Disconnecting');
|
||||||
if (flush) {
|
if (flush) {
|
||||||
_logger.info('Flushing');
|
_logger.info('Flushing');
|
||||||
|
|||||||
@@ -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: 6.1.0
|
version: 6.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
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ dependencies:
|
|||||||
path: ^1.8.2
|
path: ^1.8.2
|
||||||
path_provider: ^2.0.1
|
path_provider: ^2.0.1
|
||||||
sqlite3_flutter_libs: ^0.5.0
|
sqlite3_flutter_libs: ^0.5.0
|
||||||
stream_chat: ^6.1.0
|
stream_chat: ^6.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.3.3
|
build_runner: ^2.3.3
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ void main() {
|
|||||||
// Should match lastMessageAt date
|
// Should match lastMessageAt date
|
||||||
expect(
|
expect(
|
||||||
updatedChannel.lastMessageAt,
|
updatedChannel.lastMessageAt,
|
||||||
isSameDateAs(insertedChannel.lastMessageAt!),
|
isSameDateAs(insertedChannel.lastMessageAt),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -177,7 +177,7 @@ void main() {
|
|||||||
// Should match lastMessageAt date
|
// Should match lastMessageAt date
|
||||||
expect(
|
expect(
|
||||||
updatedChannel.lastMessageAt,
|
updatedChannel.lastMessageAt,
|
||||||
isSameDateAs(insertedChannel.lastMessageAt!),
|
isSameDateAs(insertedChannel.lastMessageAt),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -220,7 +220,7 @@ void main() {
|
|||||||
// Should match lastMessageAt date
|
// Should match lastMessageAt date
|
||||||
expect(
|
expect(
|
||||||
updatedChannel.lastMessageAt,
|
updatedChannel.lastMessageAt,
|
||||||
isSameDateAs(insertedChannel.lastMessageAt!),
|
isSameDateAs(insertedChannel.lastMessageAt),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ void main() {
|
|||||||
expect(fetchedMember.updatedAt, isSameDateAs(member.updatedAt));
|
expect(fetchedMember.updatedAt, isSameDateAs(member.updatedAt));
|
||||||
expect(
|
expect(
|
||||||
fetchedMember.inviteAcceptedAt,
|
fetchedMember.inviteAcceptedAt,
|
||||||
isSameDateAs(member.inviteAcceptedAt!),
|
isSameDateAs(member.inviteAcceptedAt),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -93,7 +93,7 @@ void main() {
|
|||||||
expect(fetchedMember.updatedAt, isSameDateAs(member.updatedAt));
|
expect(fetchedMember.updatedAt, isSameDateAs(member.updatedAt));
|
||||||
expect(
|
expect(
|
||||||
fetchedMember.inviteAcceptedAt,
|
fetchedMember.inviteAcceptedAt,
|
||||||
isSameDateAs(member.inviteAcceptedAt!),
|
isSameDateAs(member.inviteAcceptedAt),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ void main() {
|
|||||||
expect(channelModel.updatedAt, isSameDateAs(entity.updatedAt));
|
expect(channelModel.updatedAt, isSameDateAs(entity.updatedAt));
|
||||||
expect(channelModel.memberCount, entity.memberCount);
|
expect(channelModel.memberCount, entity.memberCount);
|
||||||
expect(channelModel.cid, entity.cid);
|
expect(channelModel.cid, entity.cid);
|
||||||
expect(channelModel.lastMessageAt, isSameDateAs(entity.lastMessageAt!));
|
expect(channelModel.lastMessageAt, isSameDateAs(entity.lastMessageAt));
|
||||||
expect(channelModel.deletedAt, isSameDateAs(entity.deletedAt!));
|
expect(channelModel.deletedAt, isSameDateAs(entity.deletedAt));
|
||||||
expect(channelModel.extraData, entity.extraData);
|
expect(channelModel.extraData, entity.extraData);
|
||||||
expect(channelModel.createdBy!.id, entity.createdById);
|
expect(channelModel.createdBy!.id, entity.createdById);
|
||||||
});
|
});
|
||||||
@@ -77,8 +77,8 @@ void main() {
|
|||||||
expect(channelModel.updatedAt, isSameDateAs(entity.updatedAt));
|
expect(channelModel.updatedAt, isSameDateAs(entity.updatedAt));
|
||||||
expect(channelModel.memberCount, entity.memberCount);
|
expect(channelModel.memberCount, entity.memberCount);
|
||||||
expect(channelModel.cid, entity.cid);
|
expect(channelModel.cid, entity.cid);
|
||||||
expect(channelModel.lastMessageAt, isSameDateAs(entity.lastMessageAt!));
|
expect(channelModel.lastMessageAt, isSameDateAs(entity.lastMessageAt));
|
||||||
expect(channelModel.deletedAt, isSameDateAs(entity.deletedAt!));
|
expect(channelModel.deletedAt, isSameDateAs(entity.deletedAt));
|
||||||
expect(channelModel.extraData, entity.extraData);
|
expect(channelModel.extraData, entity.extraData);
|
||||||
expect(channelModel.createdBy!.id, entity.createdById);
|
expect(channelModel.createdBy!.id, entity.createdById);
|
||||||
});
|
});
|
||||||
@@ -115,8 +115,8 @@ void main() {
|
|||||||
expect(channelEntity.updatedAt, isSameDateAs(model.updatedAt));
|
expect(channelEntity.updatedAt, isSameDateAs(model.updatedAt));
|
||||||
expect(channelEntity.memberCount, model.memberCount);
|
expect(channelEntity.memberCount, model.memberCount);
|
||||||
expect(channelEntity.cid, model.cid);
|
expect(channelEntity.cid, model.cid);
|
||||||
expect(channelEntity.lastMessageAt, isSameDateAs(model.lastMessageAt!));
|
expect(channelEntity.lastMessageAt, isSameDateAs(model.lastMessageAt));
|
||||||
expect(channelEntity.deletedAt, isSameDateAs(model.deletedAt!));
|
expect(channelEntity.deletedAt, isSameDateAs(model.deletedAt));
|
||||||
expect(channelEntity.extraData, model.extraData);
|
expect(channelEntity.extraData, model.extraData);
|
||||||
expect(channelEntity.createdById, model.createdBy!.id);
|
expect(channelEntity.createdById, model.createdBy!.id);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ void main() {
|
|||||||
expect(member.createdAt, isSameDateAs(entity.createdAt));
|
expect(member.createdAt, isSameDateAs(entity.createdAt));
|
||||||
expect(member.updatedAt, isSameDateAs(entity.updatedAt));
|
expect(member.updatedAt, isSameDateAs(entity.updatedAt));
|
||||||
expect(member.channelRole, entity.channelRole);
|
expect(member.channelRole, entity.channelRole);
|
||||||
expect(member.inviteAcceptedAt, isSameDateAs(entity.inviteAcceptedAt!));
|
expect(member.inviteAcceptedAt, isSameDateAs(entity.inviteAcceptedAt));
|
||||||
expect(member.inviteRejectedAt, isSameDateAs(entity.inviteRejectedAt!));
|
expect(member.inviteRejectedAt, isSameDateAs(entity.inviteRejectedAt));
|
||||||
expect(member.invited, entity.invited);
|
expect(member.invited, entity.invited);
|
||||||
expect(member.banned, entity.banned);
|
expect(member.banned, entity.banned);
|
||||||
expect(member.shadowBanned, entity.shadowBanned);
|
expect(member.shadowBanned, entity.shadowBanned);
|
||||||
@@ -59,8 +59,8 @@ void main() {
|
|||||||
expect(entity.createdAt, isSameDateAs(member.createdAt));
|
expect(entity.createdAt, isSameDateAs(member.createdAt));
|
||||||
expect(entity.updatedAt, isSameDateAs(member.updatedAt));
|
expect(entity.updatedAt, isSameDateAs(member.updatedAt));
|
||||||
expect(entity.channelRole, member.channelRole);
|
expect(entity.channelRole, member.channelRole);
|
||||||
expect(entity.inviteAcceptedAt, isSameDateAs(member.inviteAcceptedAt!));
|
expect(entity.inviteAcceptedAt, isSameDateAs(member.inviteAcceptedAt));
|
||||||
expect(entity.inviteRejectedAt, isSameDateAs(member.inviteRejectedAt!));
|
expect(entity.inviteRejectedAt, isSameDateAs(member.inviteRejectedAt));
|
||||||
expect(entity.invited, member.invited);
|
expect(entity.invited, member.invited);
|
||||||
expect(entity.banned, member.banned);
|
expect(entity.banned, member.banned);
|
||||||
expect(entity.shadowBanned, member.shadowBanned);
|
expect(entity.shadowBanned, member.shadowBanned);
|
||||||
|
|||||||
@@ -96,11 +96,11 @@ void main() {
|
|||||||
expect(message.updatedAt, isSameDateAs(entity.updatedAt));
|
expect(message.updatedAt, isSameDateAs(entity.updatedAt));
|
||||||
expect(message.extraData, entity.extraData);
|
expect(message.extraData, entity.extraData);
|
||||||
expect(message.user!.id, entity.userId);
|
expect(message.user!.id, entity.userId);
|
||||||
expect(message.deletedAt, isSameDateAs(entity.deletedAt!));
|
expect(message.deletedAt, isSameDateAs(entity.deletedAt));
|
||||||
expect(message.text, entity.messageText);
|
expect(message.text, entity.messageText);
|
||||||
expect(message.pinned, entity.pinned);
|
expect(message.pinned, entity.pinned);
|
||||||
expect(message.pinExpires, isSameDateAs(entity.pinExpires!));
|
expect(message.pinExpires, isSameDateAs(entity.pinExpires));
|
||||||
expect(message.pinnedAt, isSameDateAs(entity.pinnedAt!));
|
expect(message.pinnedAt, isSameDateAs(entity.pinnedAt));
|
||||||
expect(message.pinnedBy!.id, entity.pinnedByUserId);
|
expect(message.pinnedBy!.id, entity.pinnedByUserId);
|
||||||
expect(message.reactionCounts, entity.reactionCounts);
|
expect(message.reactionCounts, entity.reactionCounts);
|
||||||
expect(message.reactionScores, entity.reactionScores);
|
expect(message.reactionScores, entity.reactionScores);
|
||||||
@@ -191,11 +191,11 @@ void main() {
|
|||||||
expect(entity.updatedAt, isSameDateAs(message.updatedAt));
|
expect(entity.updatedAt, isSameDateAs(message.updatedAt));
|
||||||
expect(entity.extraData, message.extraData);
|
expect(entity.extraData, message.extraData);
|
||||||
expect(entity.userId, message.user!.id);
|
expect(entity.userId, message.user!.id);
|
||||||
expect(entity.deletedAt, isSameDateAs(message.deletedAt!));
|
expect(entity.deletedAt, isSameDateAs(message.deletedAt));
|
||||||
expect(entity.messageText, message.text);
|
expect(entity.messageText, message.text);
|
||||||
expect(entity.pinned, message.pinned);
|
expect(entity.pinned, message.pinned);
|
||||||
expect(entity.pinExpires, isSameDateAs(message.pinExpires!));
|
expect(entity.pinExpires, isSameDateAs(message.pinExpires));
|
||||||
expect(entity.pinnedAt, isSameDateAs(message.pinnedAt!));
|
expect(entity.pinnedAt, isSameDateAs(message.pinnedAt));
|
||||||
expect(entity.pinnedByUserId, message.pinnedBy!.id);
|
expect(entity.pinnedByUserId, message.pinnedBy!.id);
|
||||||
expect(entity.reactionCounts, message.reactionCounts);
|
expect(entity.reactionCounts, message.reactionCounts);
|
||||||
expect(entity.reactionScores, message.reactionScores);
|
expect(entity.reactionScores, message.reactionScores);
|
||||||
|
|||||||
@@ -89,11 +89,11 @@ void main() {
|
|||||||
expect(message.updatedAt, isSameDateAs(entity.updatedAt));
|
expect(message.updatedAt, isSameDateAs(entity.updatedAt));
|
||||||
expect(message.extraData, entity.extraData);
|
expect(message.extraData, entity.extraData);
|
||||||
expect(message.user!.id, entity.userId);
|
expect(message.user!.id, entity.userId);
|
||||||
expect(message.deletedAt, isSameDateAs(entity.deletedAt!));
|
expect(message.deletedAt, isSameDateAs(entity.deletedAt));
|
||||||
expect(message.text, entity.messageText);
|
expect(message.text, entity.messageText);
|
||||||
expect(message.pinned, entity.pinned);
|
expect(message.pinned, entity.pinned);
|
||||||
expect(message.pinExpires, isSameDateAs(entity.pinExpires!));
|
expect(message.pinExpires, isSameDateAs(entity.pinExpires));
|
||||||
expect(message.pinnedAt, isSameDateAs(entity.pinnedAt!));
|
expect(message.pinnedAt, isSameDateAs(entity.pinnedAt));
|
||||||
expect(message.pinnedBy!.id, entity.pinnedByUserId);
|
expect(message.pinnedBy!.id, entity.pinnedByUserId);
|
||||||
expect(message.reactionCounts, entity.reactionCounts);
|
expect(message.reactionCounts, entity.reactionCounts);
|
||||||
expect(message.reactionScores, entity.reactionScores);
|
expect(message.reactionScores, entity.reactionScores);
|
||||||
@@ -179,11 +179,11 @@ void main() {
|
|||||||
expect(entity.updatedAt, isSameDateAs(message.updatedAt));
|
expect(entity.updatedAt, isSameDateAs(message.updatedAt));
|
||||||
expect(entity.extraData, message.extraData);
|
expect(entity.extraData, message.extraData);
|
||||||
expect(entity.userId, message.user!.id);
|
expect(entity.userId, message.user!.id);
|
||||||
expect(entity.deletedAt, isSameDateAs(message.deletedAt!));
|
expect(entity.deletedAt, isSameDateAs(message.deletedAt));
|
||||||
expect(entity.messageText, message.text);
|
expect(entity.messageText, message.text);
|
||||||
expect(entity.pinned, message.pinned);
|
expect(entity.pinned, message.pinned);
|
||||||
expect(entity.pinExpires, isSameDateAs(message.pinExpires!));
|
expect(entity.pinExpires, isSameDateAs(message.pinExpires));
|
||||||
expect(entity.pinnedAt, isSameDateAs(message.pinnedAt!));
|
expect(entity.pinnedAt, isSameDateAs(message.pinnedAt));
|
||||||
expect(entity.pinnedByUserId, message.pinnedBy!.id);
|
expect(entity.pinnedByUserId, message.pinnedBy!.id);
|
||||||
expect(entity.reactionCounts, message.reactionCounts);
|
expect(entity.reactionCounts, message.reactionCounts);
|
||||||
expect(entity.reactionScores, message.reactionScores);
|
expect(entity.reactionScores, message.reactionScores);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ void main() {
|
|||||||
expect(user.language, entity.language);
|
expect(user.language, entity.language);
|
||||||
expect(user.createdAt, isSameDateAs(entity.createdAt));
|
expect(user.createdAt, isSameDateAs(entity.createdAt));
|
||||||
expect(user.updatedAt, isSameDateAs(entity.updatedAt));
|
expect(user.updatedAt, isSameDateAs(entity.updatedAt));
|
||||||
expect(user.lastActive, isSameDateAs(entity.lastActive!));
|
expect(user.lastActive, isSameDateAs(entity.lastActive));
|
||||||
expect(user.online, entity.online);
|
expect(user.online, entity.online);
|
||||||
expect(user.banned, entity.banned);
|
expect(user.banned, entity.banned);
|
||||||
expect(user.extraData, entity.extraData);
|
expect(user.extraData, entity.extraData);
|
||||||
@@ -52,7 +52,7 @@ void main() {
|
|||||||
expect(entity.language, user.language);
|
expect(entity.language, user.language);
|
||||||
expect(entity.createdAt, isSameDateAs(user.createdAt));
|
expect(entity.createdAt, isSameDateAs(user.createdAt));
|
||||||
expect(entity.updatedAt, isSameDateAs(user.updatedAt));
|
expect(entity.updatedAt, isSameDateAs(user.updatedAt));
|
||||||
expect(entity.lastActive, isSameDateAs(user.lastActive!));
|
expect(entity.lastActive, isSameDateAs(user.lastActive));
|
||||||
expect(entity.online, user.online);
|
expect(entity.online, user.online);
|
||||||
expect(entity.banned, user.banned);
|
expect(entity.banned, user.banned);
|
||||||
expect(entity.extraData, user.extraData);
|
expect(entity.extraData, user.extraData);
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
Matcher isSameDateAs(DateTime targetDate) =>
|
Matcher isSameDateAs(DateTime? targetDate) =>
|
||||||
_IsSameDateAs(targetDate: targetDate);
|
_IsSameDateAs(targetDate: targetDate);
|
||||||
|
|
||||||
class _IsSameDateAs extends Matcher {
|
class _IsSameDateAs extends Matcher {
|
||||||
const _IsSameDateAs({required this.targetDate});
|
const _IsSameDateAs({required this.targetDate});
|
||||||
|
|
||||||
final DateTime targetDate;
|
final DateTime? targetDate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool matches(covariant DateTime date, Map matchState) =>
|
bool matches(covariant DateTime date, Map matchState) {
|
||||||
date.year == targetDate.year &&
|
return date.year == targetDate?.year &&
|
||||||
date.month == targetDate.month &&
|
date.month == targetDate?.month &&
|
||||||
date.day == targetDate.day &&
|
date.day == targetDate?.day &&
|
||||||
date.hour == targetDate.hour &&
|
date.hour == targetDate?.hour &&
|
||||||
date.minute == targetDate.minute &&
|
date.minute == targetDate?.minute &&
|
||||||
date.second == targetDate.second;
|
date.second == targetDate?.second;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Description describe(Description description) =>
|
Description describe(Description description) =>
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ void main() {
|
|||||||
const userId = 'testUserId';
|
const userId = 'testUserId';
|
||||||
test('successfully connects with the Database', () async {
|
test('successfully connects with the Database', () async {
|
||||||
final client = StreamChatPersistenceClient(logLevel: Level.ALL);
|
final client = StreamChatPersistenceClient(logLevel: Level.ALL);
|
||||||
expect(client.db, isNull);
|
expect(client.isConnected, false);
|
||||||
await client.connect(userId, databaseProvider: testDatabaseProvider);
|
await client.connect(userId, databaseProvider: testDatabaseProvider);
|
||||||
expect(client.db, isNotNull);
|
expect(client.isConnected, true);
|
||||||
expect(client.db, isA<DriftChatDatabase>());
|
expect(client.db, isA<DriftChatDatabase>());
|
||||||
expect(client.db!.userId, userId);
|
expect(client.db!.userId, userId);
|
||||||
|
|
||||||
@@ -29,10 +29,9 @@ void main() {
|
|||||||
|
|
||||||
test('throws if already connected', () async {
|
test('throws if already connected', () async {
|
||||||
final client = StreamChatPersistenceClient(logLevel: Level.ALL);
|
final client = StreamChatPersistenceClient(logLevel: Level.ALL);
|
||||||
expect(client.db, isNull);
|
expect(client.isConnected, false);
|
||||||
await client.connect(userId, databaseProvider: testDatabaseProvider);
|
await client.connect(userId, databaseProvider: testDatabaseProvider);
|
||||||
expect(client.db, isNotNull);
|
expect(client.isConnected, true);
|
||||||
expect(client.db, isNotNull);
|
|
||||||
expect(client.db, isA<DriftChatDatabase>());
|
expect(client.db, isA<DriftChatDatabase>());
|
||||||
expect(client.db!.userId, userId);
|
expect(client.db!.userId, userId);
|
||||||
expect(
|
expect(
|
||||||
@@ -50,9 +49,9 @@ void main() {
|
|||||||
const userId = 'testUserId';
|
const userId = 'testUserId';
|
||||||
final client = StreamChatPersistenceClient(logLevel: Level.ALL);
|
final client = StreamChatPersistenceClient(logLevel: Level.ALL);
|
||||||
await client.connect(userId, databaseProvider: testDatabaseProvider);
|
await client.connect(userId, databaseProvider: testDatabaseProvider);
|
||||||
expect(client.db, isNotNull);
|
expect(client.isConnected, true);
|
||||||
await client.disconnect(flush: true);
|
await client.disconnect(flush: true);
|
||||||
expect(client.db, isNull);
|
expect(client.isConnected, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('client function throws stateError if db is not yet connected', () {
|
test('client function throws stateError if db is not yet connected', () {
|
||||||
|
|||||||
Reference in New Issue
Block a user