Merge branch 'develop'

This commit is contained in:
Salvatore Giordano
2022-05-20 13:11:28 +02:00
210 changed files with 1361 additions and 977 deletions
+6 -6
View File
@@ -39,36 +39,36 @@ jobs:
run: melos bootstrap run: melos bootstrap
- name: "Stream Chat Metrics" - name: "Stream Chat Metrics"
uses: dart-code-checker/dart-code-metrics-action@v1 uses: dart-code-checker/dart-code-metrics-action@v2.0.0
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
relative_path: 'packages/stream_chat' relative_path: 'packages/stream_chat'
folders: ${{ env.folders }} folders: ${{ env.folders }}
- name: "Stream Chat Flutter Core Metrics" - name: "Stream Chat Flutter Core Metrics"
uses: dart-code-checker/dart-code-metrics-action@v1 uses: dart-code-checker/dart-code-metrics-action@v2.0.0
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
relative_path: 'packages/stream_chat_flutter_core' relative_path: 'packages/stream_chat_flutter_core'
folders: ${{ env.folders }} folders: ${{ env.folders }}
- name: "Stream Chat Flutter Metrics" - name: "Stream Chat Flutter Metrics"
uses: dart-code-checker/dart-code-metrics-action@v1 uses: dart-code-checker/dart-code-metrics-action@v2.0.0
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
relative_path: 'packages/stream_chat_flutter' relative_path: 'packages/stream_chat_flutter'
folders: ${{ env.folders }} folders: ${{ env.folders }}
- name: "Stream Chat Localizations Metrics" - name: "Stream Chat Localizations Metrics"
uses: dart-code-checker/dart-code-metrics-action@v1 uses: dart-code-checker/dart-code-metrics-action@v2.0.0
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
relative_path: 'packages/stream_chat_localizations' relative_path: 'packages/stream_chat_localizations'
folders: ${{ env.folders }} folders: ${{ env.folders }}
- name: "Stream Chat Persistence Metrics" - name: "Stream Chat Persistence Metrics"
uses: dart-code-checker/dart-code-metrics-action@v1 uses: dart-code-checker/dart-code-metrics-action@v2.0.0
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
relative_path: 'packages/stream_chat_persistence' relative_path: 'packages/stream_chat_persistence'
folders: ${{ env.folders }} folders: ${{ env.folders }}
@@ -3,4 +3,4 @@
# Fast fail the script on failures. # Fast fail the script on failures.
set -e set -e
pub global run remove_from_coverage:remove_from_coverage -f coverage/lcov.info -r '\.g\.dart$' -r '\.freezed\.dart$' flutter pub global run remove_from_coverage:remove_from_coverage -f coverage/lcov.info -r '\.g\.dart$' -r '\.freezed\.dart$'
@@ -94,7 +94,7 @@ jobs:
- name: "Install Tools" - name: "Install Tools"
run: | run: |
flutter pub global activate melos ${{ env.melos_version }} flutter pub global activate melos ${{ env.melos_version }}
pub global activate remove_from_coverage flutter pub global activate remove_from_coverage
- name: "Bootstrap Workspace" - name: "Bootstrap Workspace"
run: melos bootstrap run: melos bootstrap
- name: "Flutter Test" - name: "Flutter Test"
+15
View File
@@ -147,6 +147,21 @@ linter:
- tighten_type_of_initializing_formals - tighten_type_of_initializing_formals
- null_check_on_nullable_type_parameter - null_check_on_nullable_type_parameter
- conditional_uri_does_not_exist
- secure_pubspec_urls
- sized_box_shrink_expand
- unnecessary_constructor_name
- unnecessary_late
- use_colored_box
- use_decorated_box
- use_enums
- use_super_parameters
- avoid_escaping_inner_quotes
- unnecessary_overrides
- prefer_null_aware_method_calls
- use_named_constants
- use_raw_strings
# https://dartcodemetrics.dev/docs/getting-started/introduction # https://dartcodemetrics.dev/docs/getting-started/introduction
dart_code_metrics: dart_code_metrics:
rules: rules:
@@ -0,0 +1,144 @@
---
id: error_reporting_with_sentry
sidebar_position: 15
title: Error Reporting With Sentry
---
Error Reporting With Sentry
## Introduction
While one always tries to create apps that are free of bugs, they're sure to crop up from time to time. Since buggy apps lead to unhappy users and customers, it's important to understand how often your users experience bugs and where those bugs occur. That way, you can prioritize the bugs with the highest impact and work to fix them.
Whenever an error occurs, create a report containing the error that occurred and the associated stack trace. You can then send the report to an error tracking service, such as [Sentry](https://sentry.io/), [Rollbar](https://rollbar.com/), or [Firebase Crashlytics](https://firebase.google.com/docs/crashlytics).
The error tracking service aggregates all of the crashes your users experience and groups them together. This allows you to know how often your app fails and where your users run into trouble.
In this guide, learn how to report Stream Chat errors to the [Sentry](https://sentry.io/welcome/) crash reporting service using the following steps.
### 1. Get a DSN From Sentry
Before reporting errors to Sentry, you need a “DSN” to uniquely identify your app with the Sentry service:
To get a DSN, use the following steps:
- [Create an account with Sentry](https://sentry.io/signup/).
- Log in to the account.
- Create a new Flutter project.
- Copy the code snippet that includes the DSN.
### 2. Import the Sentry package
Import the `sentry_flutter` package into your app. The sentry package makes it easier to send error reports to the Sentry error tracking service.
```yaml
dependencies:
sentry_flutter: <latest_version>
```
### 3. Initialize the Sentry SDK
Initialize the SDK to capture different unhandled errors automatically.
```dart
import 'package:sentry_flutter/sentry_flutter.dart';
Future<void> main() async {
await SentryFlutter.init(
(options) => options.dsn = 'https://[email protected]/example',
appRunner: () => runApp(const MyApp()),
);
}
```
Or, if you want to run your app in your own error zone, use `runZonedGuarded`:
```dart
void main() async {
/// Captures errors reported by the Flutter framework.
FlutterError.onError = (FlutterErrorDetails details) {
if (kDebugMode) {
// In development mode, simply print to console.
FlutterError.dumpErrorToConsole(details);
} else {
// In production mode, report to the application zone to report to Sentry.
Zone.current.handleUncaughtError(details.exception, details.stack!);
}
};
Future<void> _reportError(dynamic error, StackTrace stackTrace) async {
// Print the exception to the console.
if (kDebugMode) {
// Print the full stack trace in debug mode.
print(stackTrace);
return;
} else {
// Send the Exception and Stacktrace to sentry in Production mode.
await Sentry.captureException(error, stackTrace: stackTrace);
}
}
runZonedGuarded(
() async {
await SentryFlutter.init(
(options) => options.dsn = 'https://[email protected]/example',
);
runApp(const MyApp());
},
_reportError,
);
}
```
Alternatively, you can pass the DSN to Flutter using the **dart-define** tag:
```bash
--dart-define SENTRY_DSN=https://[email protected]/example
```
### 4. Integration With StreamChat Applications
Override the default `logHandlerFunction` to send errors to Sentry.
```dart
void sampleAppLogHandler(LogRecord record) async {
if (kDebugMode) StreamChatClient.defaultLogHandler(record);
// Report errors to Sentry
if (record.error != null || record.stackTrace != null) {
await Sentry.captureException(
record.error,
stackTrace: record.stackTrace,
);
}
}
StreamChatClient buildStreamChatClient(
String apiKey, {
Level logLevel = Level.SEVERE,
}) {
return StreamChatClient(
apiKey,
logLevel: logLevel,
logHandlerFunction: sampleAppLogHandler, // Pass the overridden logHandlerFunction
);
}
```
### 5. Capture Errors Programmatically
Besides the automatic error reporting that Sentry generates by importing and initializing the SDK,
you can use the API to manually report errors to Sentry:
```dart
await Sentry.captureException(exception, stackTrace: stackTrace);
```
For more information, see the [Sentry API](https://pub.dev/documentation/sentry_flutter/latest/sentry_flutter/sentry_flutter-library.html) docs on Pub.
### Complete Example
To view a working example, see the [Stream Sample app](https://github.com/GetStream/flutter-samples/tree/main/packages/stream_chat_v1).
### Learn More
Extensive documentation about using the Sentry SDK can be found on [Sentry's site](https://docs.sentry.io/platforms/flutter/).
View File
+13 -2
View File
@@ -1,4 +1,5 @@
name: stream_chat_flutter name: stream_chat_flutter
repository: https://github.com/GetStream/stream-chat-flutter
versioning: versioning:
mode: independent mode: independent
@@ -7,6 +8,10 @@ packages:
- packages/** - packages/**
scripts: scripts:
postclean:
run: melos run clean:flutter --no-select
description: Runs "flutter clean" in all Flutter packages
lint:all: lint:all:
run: melos run analyze && melos run format run: melos run analyze && melos run format
description: Run all static analysis checks description: Run all static analysis checks
@@ -74,6 +79,12 @@ scripts:
flutter: true flutter: true
dir-exists: test dir-exists: test
clean:flutter:
run: melos exec -c 4 --fail-fast -- "flutter clean"
description: Run Flutter clean for a specific package in this project.
select-package:
flutter: true
coverage:ignore-file: coverage:ignore-file:
run: | run: |
melos exec -c 5 --fail-fast -- "\$MELOS_ROOT_PATH/.github/workflows/scripts/remove-from-coverage.sh" melos exec -c 5 --fail-fast -- "\$MELOS_ROOT_PATH/.github/workflows/scripts/remove-from-coverage.sh"
@@ -91,5 +102,5 @@ dev_dependencies:
dart_code_metrics: ^4.4.0 dart_code_metrics: ^4.4.0
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.17.0 <3.0.0'
flutter: '>=1.17.0 <2.0.0' flutter: '>=1.17.0 <3.0.0'
+22
View File
@@ -1,3 +1,25 @@
## 4.2.0
✅ Added
- Added `PaginationParams.createdAtAfterOrEqual` for message pagination.
- Added `PaginationParams.createdAtAfter` for message pagination.
- Added `PaginationParams.createdAtBeforeOrEqual` for message pagination.
- Added `PaginationParams.createdAtBefore` for message pagination.
- Added `PaginationParams.createdAtAround` for message pagination.
- Added support for `channel.disabled`, `channel.hidden` and `channel.truncatedAt` in `Channel`.
- Added support for `channel.membership` and `channel.membershipStream` in `Channel`.
- `Channel` now listens for `member.updated` events and updates the `Channel.members` accordingly.
🔄 Changed
- Deprecated `PaginationParams.before` and `PaginationParams.after`. Use `PaginationParams.limit` instead.
🐞 Fixed
- [[#1147]](https://github.com/GetStream/stream-chat-flutter/issues/1147) `channel.unset` not updating the extra data
stream.
## 4.1.0 ## 4.1.0
✅ Added ✅ Added
+6 -6
View File
@@ -43,10 +43,10 @@ class StreamExample extends StatelessWidget {
/// To initialize this example, an instance of /// To initialize this example, an instance of
/// [client] and [channel] is required. /// [client] and [channel] is required.
const StreamExample({ const StreamExample({
Key? key, super.key,
required this.client, required this.client,
required this.channel, required this.channel,
}) : super(key: key); });
/// Instance of [StreamChatClient] we created earlier. /// Instance of [StreamChatClient] we created earlier.
/// This contains information about our application and connection state. /// This contains information about our application and connection state.
@@ -67,9 +67,9 @@ class StreamExample extends StatelessWidget {
class HomeScreen extends StatelessWidget { class HomeScreen extends StatelessWidget {
/// [HomeScreen] is constructed using the [Channel] we defined earlier. /// [HomeScreen] is constructed using the [Channel] we defined earlier.
const HomeScreen({ const HomeScreen({
Key? key, super.key,
required this.channel, required this.channel,
}) : super(key: key); });
/// Channel object containing the [Channel.id] we'd like to observe. /// Channel object containing the [Channel.id] we'd like to observe.
final Channel channel; final Channel channel;
@@ -119,10 +119,10 @@ class HomeScreen extends StatelessWidget {
class MessageView extends StatefulWidget { class MessageView extends StatefulWidget {
/// Message takes the latest list of messages and the current channel. /// Message takes the latest list of messages and the current channel.
const MessageView({ const MessageView({
Key? key, super.key,
required this.messages, required this.messages,
required this.channel, required this.channel,
}) : super(key: key); });
/// List of messages sent in the given channel. /// List of messages sent in the given channel.
final List<Message> messages; final List<Message> messages;
+1 -1
View File
@@ -5,7 +5,7 @@ publish_to: "none"
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.17.0 <3.0.0'
dependencies: dependencies:
cupertino_icons: ^1.0.0 cupertino_icons: ^1.0.0
@@ -171,6 +171,18 @@ class Channel {
return state!.channelStateStream.map((cs) => cs.channel?.config); return state!.channelStateStream.map((cs) => cs.channel?.config);
} }
/// Relationship of the current user to this channel.
Member? get membership {
_checkInitialized();
return state!._channelState.membership;
}
/// Relationship of the current user to this channel as a stream.
Stream<Member?> get membershipStream {
_checkInitialized();
return state!.channelStateStream.map((cs) => cs.membership);
}
/// Channel user creator. /// Channel user creator.
User? get createdBy { User? get createdBy {
_checkInitialized(); _checkInitialized();
@@ -195,6 +207,42 @@ class Channel {
return state!.channelStateStream.map((cs) => cs.channel?.frozen == true); return state!.channelStateStream.map((cs) => cs.channel?.frozen == true);
} }
/// Channel disabled status.
bool get disabled {
_checkInitialized();
return state!._channelState.channel?.disabled == true;
}
/// Channel disabled status as a stream.
Stream<bool> get disabledStream {
_checkInitialized();
return state!.channelStateStream.map((cs) => cs.channel?.disabled == true);
}
/// Channel hidden status.
bool get hidden {
_checkInitialized();
return state!._channelState.channel?.hidden == true;
}
/// Channel hidden status as a stream.
Stream<bool> get hiddenStream {
_checkInitialized();
return state!.channelStateStream.map((cs) => cs.channel?.hidden == true);
}
/// The last date at which the channel got truncated.
DateTime? get truncatedAt {
_checkInitialized();
return state!._channelState.channel?.truncatedAt;
}
/// The last date at which the channel got truncated as a stream.
Stream<DateTime?> get truncatedAtStream {
_checkInitialized();
return state!.channelStateStream.map((cs) => cs.channel?.truncatedAt);
}
/// Cooldown count /// Cooldown count
int get cooldown { int get cooldown {
_checkInitialized(); _checkInitialized();
@@ -1540,6 +1588,8 @@ class ChannelClientState {
_listenMemberRemoved(); _listenMemberRemoved();
_listenMemberUpdated();
_listenMemberBanned(); _listenMemberBanned();
_listenMemberUnbanned(); _listenMemberUnbanned();
@@ -1632,6 +1682,18 @@ class ChannelClientState {
})); }));
} }
void _listenMemberUpdated() {
_subscriptions.add(_channel.on(EventType.memberUpdated).listen((Event e) {
final member = e.member;
final existingMembers = channelState.members ?? [];
updateChannelState(channelState.copyWith(
members: existingMembers
.map((m) => m.userId == member!.userId ? member : m)
.toList(growable: false),
));
}));
}
void _listenChannelUpdated() { void _listenChannelUpdated() {
_subscriptions.add(_channel.on(EventType.channelUpdated).listen((Event e) { _subscriptions.add(_channel.on(EventType.channelUpdated).listen((Event e) {
final channel = e.channel!; final channel = e.channel!;
@@ -2173,7 +2235,7 @@ class ChannelClientState {
/// The channel threads related to this channel. /// The channel threads related to this channel.
Map<String, List<Message>> get threads => Map<String, List<Message>> get threads =>
_threadsController.value.map((key, value) => MapEntry(key, value)); _threadsController.value.map(MapEntry.new);
/// The channel threads related to this channel as a stream. /// The channel threads related to this channel as a stream.
Stream<Map<String, List<Message>>> get threadsStream => Stream<Map<String, List<Message>>> get threadsStream =>
@@ -10,15 +10,6 @@ enum PushProvider {
apn, apn,
} }
/// Helper extension for [PushProvider]
extension PushProviderX on PushProvider {
/// Returns the string notion for [PushProvider].
String get name => {
PushProvider.apn: 'apn',
PushProvider.firebase: 'firebase',
}[this]!;
}
/// Defines the api dedicated to device operations /// Defines the api dedicated to device operations
class DeviceApi { class DeviceApi {
/// Initialize a new device api /// Initialize a new device api
@@ -1,3 +1,5 @@
// ignore_for_file: deprecated_member_use_from_same_package
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
@@ -69,6 +71,11 @@ class PaginationParams extends Equatable {
this.greaterThanOrEqual, this.greaterThanOrEqual,
this.lessThan, this.lessThan,
this.lessThanOrEqual, this.lessThanOrEqual,
this.createdAtAfterOrEqual,
this.createdAtAfter,
this.createdAtBeforeOrEqual,
this.createdAtBefore,
this.createdAtAround,
}) : assert( }) : assert(
offset == null || offset == 0 || next == null, offset == null || offset == 0 || next == null,
'Cannot specify non-zero `offset` with `next` parameter', 'Cannot specify non-zero `offset` with `next` parameter',
@@ -82,9 +89,11 @@ class PaginationParams extends Equatable {
final int limit; final int limit;
/// The amount of items requested before message ID from the APIs. /// The amount of items requested before message ID from the APIs.
@Deprecated('before is deprecated, use limit instead')
final int before; final int before;
/// The amount of items requested after message ID from the APIs. /// The amount of items requested after message ID from the APIs.
@Deprecated('after is deprecated, use limit instead')
final int after; final int after;
/// The offset of requesting items. /// The offset of requesting items.
@@ -113,6 +122,26 @@ class PaginationParams extends Equatable {
@JsonKey(name: 'id_lte') @JsonKey(name: 'id_lte')
final String? lessThanOrEqual; final String? lessThanOrEqual;
/// Filter on createdAt greater than or equal the given value.
@JsonKey(name: 'created_at_after_or_equal')
final DateTime? createdAtAfterOrEqual;
/// Filter on createdAt greater than the given value.
@JsonKey(name: 'created_at_after')
final DateTime? createdAtAfter;
/// Filter on createdAt smaller than or equal the given value.
@JsonKey(name: 'created_at_before_or_equal')
final DateTime? createdAtBeforeOrEqual;
/// Filter on createdAt smaller than the given value.
@JsonKey(name: 'created_at_before')
final DateTime? createdAtBefore;
/// Filter on createdAt around the given value.
@JsonKey(name: 'created_at_around')
final DateTime? createdAtAround;
/// Serialize model to json /// Serialize model to json
Map<String, dynamic> toJson() => _$PaginationParamsToJson(this); Map<String, dynamic> toJson() => _$PaginationParamsToJson(this);
@@ -128,6 +157,11 @@ class PaginationParams extends Equatable {
String? greaterThanOrEqual, String? greaterThanOrEqual,
String? lessThan, String? lessThan,
String? lessThanOrEqual, String? lessThanOrEqual,
DateTime? createdAtAfterOrEqual,
DateTime? createdAtAfter,
DateTime? createdAtBeforeOrEqual,
DateTime? createdAtBefore,
DateTime? createdAtAround,
}) => }) =>
PaginationParams( PaginationParams(
limit: limit ?? this.limit, limit: limit ?? this.limit,
@@ -140,6 +174,13 @@ class PaginationParams extends Equatable {
greaterThanOrEqual: greaterThanOrEqual ?? this.greaterThanOrEqual, greaterThanOrEqual: greaterThanOrEqual ?? this.greaterThanOrEqual,
lessThan: lessThan ?? this.lessThan, lessThan: lessThan ?? this.lessThan,
lessThanOrEqual: lessThanOrEqual ?? this.lessThanOrEqual, lessThanOrEqual: lessThanOrEqual ?? this.lessThanOrEqual,
createdAtAfterOrEqual:
createdAtAfterOrEqual ?? this.createdAtAfterOrEqual,
createdAtAfter: createdAtAfter ?? this.createdAtAfter,
createdAtBeforeOrEqual:
createdAtBeforeOrEqual ?? this.createdAtBeforeOrEqual,
createdAtBefore: createdAtBefore ?? this.createdAtBefore,
createdAtAround: createdAtAround ?? this.createdAtAround,
); );
@override @override
@@ -30,6 +30,21 @@ PaginationParams _$PaginationParamsFromJson(Map<String, dynamic> json) =>
greaterThanOrEqual: json['id_gte'] as String?, greaterThanOrEqual: json['id_gte'] as String?,
lessThan: json['id_lt'] as String?, lessThan: json['id_lt'] as String?,
lessThanOrEqual: json['id_lte'] as String?, lessThanOrEqual: json['id_lte'] as String?,
createdAtAfterOrEqual: json['created_at_after_or_equal'] == null
? null
: DateTime.parse(json['created_at_after_or_equal'] as String),
createdAtAfter: json['created_at_after'] == null
? null
: DateTime.parse(json['created_at_after'] as String),
createdAtBeforeOrEqual: json['created_at_before_or_equal'] == null
? null
: DateTime.parse(json['created_at_before_or_equal'] as String),
createdAtBefore: json['created_at_before'] == null
? null
: DateTime.parse(json['created_at_before'] as String),
createdAtAround: json['created_at_around'] == null
? null
: DateTime.parse(json['created_at_around'] as String),
); );
Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) { Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) {
@@ -52,6 +67,15 @@ Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) {
writeNotNull('id_gte', instance.greaterThanOrEqual); writeNotNull('id_gte', instance.greaterThanOrEqual);
writeNotNull('id_lt', instance.lessThan); writeNotNull('id_lt', instance.lessThan);
writeNotNull('id_lte', instance.lessThanOrEqual); writeNotNull('id_lte', instance.lessThanOrEqual);
writeNotNull('created_at_after_or_equal',
instance.createdAtAfterOrEqual?.toIso8601String());
writeNotNull('created_at_after', instance.createdAtAfter?.toIso8601String());
writeNotNull('created_at_before_or_equal',
instance.createdAtBeforeOrEqual?.toIso8601String());
writeNotNull(
'created_at_before', instance.createdAtBefore?.toIso8601String());
writeNotNull(
'created_at_around', instance.createdAtAround?.toIso8601String());
return val; return val;
} }
@@ -21,9 +21,9 @@ class StreamChatError with EquatableMixin implements Exception {
class StreamWebSocketError extends StreamChatError { class StreamWebSocketError extends StreamChatError {
/// ///
const StreamWebSocketError( const StreamWebSocketError(
String message, { super.message, {
this.data, this.data,
}) : super(message); });
/// ///
factory StreamWebSocketError.fromStreamError(Map<String, Object?> error) { factory StreamWebSocketError.fromStreamError(Map<String, Object?> error) {
@@ -36,7 +36,7 @@ class AuthInterceptor extends Interceptor {
final params = {'user_id': token.userId}; final params = {'user_id': token.userId};
final headers = { final headers = {
'Authorization': token.rawValue, 'Authorization': token.rawValue,
'stream-auth-type': token.authType.raw, 'stream-auth-type': token.authType.name,
}; };
options options
..queryParameters.addAll(params) ..queryParameters.addAll(params)
@@ -6,14 +6,11 @@ class StreamChatDioError extends DioError {
/// Initialize a stream chat dio error /// Initialize a stream chat dio error
StreamChatDioError({ StreamChatDioError({
required this.error, required this.error,
required RequestOptions requestOptions, required super.requestOptions,
Response? response, super.response,
DioErrorType type = DioErrorType.other, super.type,
}) : super( }) : super(
error: error, error: error,
requestOptions: requestOptions,
response: response,
type: type,
); );
@override @override
@@ -18,15 +18,6 @@ enum AuthType {
anonymous, anonymous,
} }
/// Extension for returning the AuthType as a string
extension AuthTypeX on AuthType {
/// Returns the AuthType as a string
String get raw => {
AuthType.jwt: 'jwt',
AuthType.anonymous: 'anonymous',
}[this]!;
}
/// Token designed to store the JWT and the user it is related to. /// Token designed to store the JWT and the user it is related to.
class Token extends Equatable { class Token extends Equatable {
const Token._({ const Token._({
@@ -90,6 +90,9 @@ class AttachmentFile {
/// Union class to hold various [UploadState] of a attachment. /// Union class to hold various [UploadState] of a attachment.
@freezed @freezed
class UploadState with _$UploadState { class UploadState with _$UploadState {
// Dummy private constructor in order to use getters
const UploadState._();
/// Preparing state of the union /// Preparing state of the union
const factory UploadState.preparing() = Preparing; const factory UploadState.preparing() = Preparing;
@@ -108,10 +111,7 @@ class UploadState with _$UploadState {
/// Creates a new instance from a json /// Creates a new instance from a json
factory UploadState.fromJson(Map<String, dynamic> json) => factory UploadState.fromJson(Map<String, dynamic> json) =>
_$UploadStateFromJson(json); _$UploadStateFromJson(json);
}
/// Helper extension for UploadState
extension UploadStateX on UploadState? {
/// Returns true if state is [Preparing] /// Returns true if state is [Preparing]
bool get isPreparing => this is Preparing; bool get isPreparing => this is Preparing;
@@ -103,25 +103,29 @@ class _$UploadStateCopyWithImpl<$Res> implements $UploadStateCopyWith<$Res> {
} }
/// @nodoc /// @nodoc
abstract class $PreparingCopyWith<$Res> { abstract class _$$PreparingCopyWith<$Res> {
factory $PreparingCopyWith(Preparing value, $Res Function(Preparing) then) = factory _$$PreparingCopyWith(
_$PreparingCopyWithImpl<$Res>; _$Preparing value, $Res Function(_$Preparing) then) =
__$$PreparingCopyWithImpl<$Res>;
} }
/// @nodoc /// @nodoc
class _$PreparingCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> class __$$PreparingCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
implements $PreparingCopyWith<$Res> { implements _$$PreparingCopyWith<$Res> {
_$PreparingCopyWithImpl(Preparing _value, $Res Function(Preparing) _then) __$$PreparingCopyWithImpl(
: super(_value, (v) => _then(v as Preparing)); _$Preparing _value, $Res Function(_$Preparing) _then)
: super(_value, (v) => _then(v as _$Preparing));
@override @override
Preparing get _value => super._value as Preparing; _$Preparing get _value => super._value as _$Preparing;
} }
/// @nodoc /// @nodoc
@JsonSerializable() @JsonSerializable()
class _$Preparing implements Preparing { class _$Preparing extends Preparing {
const _$Preparing({final String? $type}) : $type = $type ?? 'preparing'; const _$Preparing({final String? $type})
: $type = $type ?? 'preparing',
super._();
factory _$Preparing.fromJson(Map<String, dynamic> json) => factory _$Preparing.fromJson(Map<String, dynamic> json) =>
_$$PreparingFromJson(json); _$$PreparingFromJson(json);
@@ -137,7 +141,7 @@ class _$Preparing implements Preparing {
@override @override
bool operator ==(dynamic other) { bool operator ==(dynamic other) {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && other is Preparing); (other.runtimeType == runtimeType && other is _$Preparing);
} }
@JsonKey(ignore: true) @JsonKey(ignore: true)
@@ -224,35 +228,37 @@ class _$Preparing implements Preparing {
} }
} }
abstract class Preparing implements UploadState { abstract class Preparing extends UploadState {
const factory Preparing() = _$Preparing; const factory Preparing() = _$Preparing;
const Preparing._() : super._();
factory Preparing.fromJson(Map<String, dynamic> json) = _$Preparing.fromJson; factory Preparing.fromJson(Map<String, dynamic> json) = _$Preparing.fromJson;
} }
/// @nodoc /// @nodoc
abstract class $InProgressCopyWith<$Res> { abstract class _$$InProgressCopyWith<$Res> {
factory $InProgressCopyWith( factory _$$InProgressCopyWith(
InProgress value, $Res Function(InProgress) then) = _$InProgress value, $Res Function(_$InProgress) then) =
_$InProgressCopyWithImpl<$Res>; __$$InProgressCopyWithImpl<$Res>;
$Res call({int uploaded, int total}); $Res call({int uploaded, int total});
} }
/// @nodoc /// @nodoc
class _$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> class __$$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
implements $InProgressCopyWith<$Res> { implements _$$InProgressCopyWith<$Res> {
_$InProgressCopyWithImpl(InProgress _value, $Res Function(InProgress) _then) __$$InProgressCopyWithImpl(
: super(_value, (v) => _then(v as InProgress)); _$InProgress _value, $Res Function(_$InProgress) _then)
: super(_value, (v) => _then(v as _$InProgress));
@override @override
InProgress get _value => super._value as InProgress; _$InProgress get _value => super._value as _$InProgress;
@override @override
$Res call({ $Res call({
Object? uploaded = freezed, Object? uploaded = freezed,
Object? total = freezed, Object? total = freezed,
}) { }) {
return _then(InProgress( return _then(_$InProgress(
uploaded: uploaded == freezed uploaded: uploaded == freezed
? _value.uploaded ? _value.uploaded
: uploaded // ignore: cast_nullable_to_non_nullable : uploaded // ignore: cast_nullable_to_non_nullable
@@ -267,10 +273,11 @@ class _$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
/// @nodoc /// @nodoc
@JsonSerializable() @JsonSerializable()
class _$InProgress implements InProgress { class _$InProgress extends InProgress {
const _$InProgress( const _$InProgress(
{required this.uploaded, required this.total, final String? $type}) {required this.uploaded, required this.total, final String? $type})
: $type = $type ?? 'inProgress'; : $type = $type ?? 'inProgress',
super._();
factory _$InProgress.fromJson(Map<String, dynamic> json) => factory _$InProgress.fromJson(Map<String, dynamic> json) =>
_$$InProgressFromJson(json); _$$InProgressFromJson(json);
@@ -292,7 +299,7 @@ class _$InProgress implements InProgress {
bool operator ==(dynamic other) { bool operator ==(dynamic other) {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is InProgress && other is _$InProgress &&
const DeepCollectionEquality().equals(other.uploaded, uploaded) && const DeepCollectionEquality().equals(other.uploaded, uploaded) &&
const DeepCollectionEquality().equals(other.total, total)); const DeepCollectionEquality().equals(other.total, total));
} }
@@ -306,8 +313,8 @@ class _$InProgress implements InProgress {
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
$InProgressCopyWith<InProgress> get copyWith => _$$InProgressCopyWith<_$InProgress> get copyWith =>
_$InProgressCopyWithImpl<InProgress>(this, _$identity); __$$InProgressCopyWithImpl<_$InProgress>(this, _$identity);
@override @override
@optionalTypeArgs @optionalTypeArgs
@@ -389,9 +396,10 @@ class _$InProgress implements InProgress {
} }
} }
abstract class InProgress implements UploadState { abstract class InProgress extends UploadState {
const factory InProgress( const factory InProgress(
{required final int uploaded, required final int total}) = _$InProgress; {required final int uploaded, required final int total}) = _$InProgress;
const InProgress._() : super._();
factory InProgress.fromJson(Map<String, dynamic> json) = factory InProgress.fromJson(Map<String, dynamic> json) =
_$InProgress.fromJson; _$InProgress.fromJson;
@@ -399,30 +407,32 @@ abstract class InProgress implements UploadState {
int get uploaded => throw _privateConstructorUsedError; int get uploaded => throw _privateConstructorUsedError;
int get total => throw _privateConstructorUsedError; int get total => throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
$InProgressCopyWith<InProgress> get copyWith => _$$InProgressCopyWith<_$InProgress> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
/// @nodoc /// @nodoc
abstract class $SuccessCopyWith<$Res> { abstract class _$$SuccessCopyWith<$Res> {
factory $SuccessCopyWith(Success value, $Res Function(Success) then) = factory _$$SuccessCopyWith(_$Success value, $Res Function(_$Success) then) =
_$SuccessCopyWithImpl<$Res>; __$$SuccessCopyWithImpl<$Res>;
} }
/// @nodoc /// @nodoc
class _$SuccessCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> class __$$SuccessCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
implements $SuccessCopyWith<$Res> { implements _$$SuccessCopyWith<$Res> {
_$SuccessCopyWithImpl(Success _value, $Res Function(Success) _then) __$$SuccessCopyWithImpl(_$Success _value, $Res Function(_$Success) _then)
: super(_value, (v) => _then(v as Success)); : super(_value, (v) => _then(v as _$Success));
@override @override
Success get _value => super._value as Success; _$Success get _value => super._value as _$Success;
} }
/// @nodoc /// @nodoc
@JsonSerializable() @JsonSerializable()
class _$Success implements Success { class _$Success extends Success {
const _$Success({final String? $type}) : $type = $type ?? 'success'; const _$Success({final String? $type})
: $type = $type ?? 'success',
super._();
factory _$Success.fromJson(Map<String, dynamic> json) => factory _$Success.fromJson(Map<String, dynamic> json) =>
_$$SuccessFromJson(json); _$$SuccessFromJson(json);
@@ -438,7 +448,7 @@ class _$Success implements Success {
@override @override
bool operator ==(dynamic other) { bool operator ==(dynamic other) {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && other is Success); (other.runtimeType == runtimeType && other is _$Success);
} }
@JsonKey(ignore: true) @JsonKey(ignore: true)
@@ -525,33 +535,34 @@ class _$Success implements Success {
} }
} }
abstract class Success implements UploadState { abstract class Success extends UploadState {
const factory Success() = _$Success; const factory Success() = _$Success;
const Success._() : super._();
factory Success.fromJson(Map<String, dynamic> json) = _$Success.fromJson; factory Success.fromJson(Map<String, dynamic> json) = _$Success.fromJson;
} }
/// @nodoc /// @nodoc
abstract class $FailedCopyWith<$Res> { abstract class _$$FailedCopyWith<$Res> {
factory $FailedCopyWith(Failed value, $Res Function(Failed) then) = factory _$$FailedCopyWith(_$Failed value, $Res Function(_$Failed) then) =
_$FailedCopyWithImpl<$Res>; __$$FailedCopyWithImpl<$Res>;
$Res call({String error}); $Res call({String error});
} }
/// @nodoc /// @nodoc
class _$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> class __$$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
implements $FailedCopyWith<$Res> { implements _$$FailedCopyWith<$Res> {
_$FailedCopyWithImpl(Failed _value, $Res Function(Failed) _then) __$$FailedCopyWithImpl(_$Failed _value, $Res Function(_$Failed) _then)
: super(_value, (v) => _then(v as Failed)); : super(_value, (v) => _then(v as _$Failed));
@override @override
Failed get _value => super._value as Failed; _$Failed get _value => super._value as _$Failed;
@override @override
$Res call({ $Res call({
Object? error = freezed, Object? error = freezed,
}) { }) {
return _then(Failed( return _then(_$Failed(
error: error == freezed error: error == freezed
? _value.error ? _value.error
: error // ignore: cast_nullable_to_non_nullable : error // ignore: cast_nullable_to_non_nullable
@@ -562,9 +573,10 @@ class _$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
/// @nodoc /// @nodoc
@JsonSerializable() @JsonSerializable()
class _$Failed implements Failed { class _$Failed extends Failed {
const _$Failed({required this.error, final String? $type}) const _$Failed({required this.error, final String? $type})
: $type = $type ?? 'failed'; : $type = $type ?? 'failed',
super._();
factory _$Failed.fromJson(Map<String, dynamic> json) => factory _$Failed.fromJson(Map<String, dynamic> json) =>
_$$FailedFromJson(json); _$$FailedFromJson(json);
@@ -584,7 +596,7 @@ class _$Failed implements Failed {
bool operator ==(dynamic other) { bool operator ==(dynamic other) {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is Failed && other is _$Failed &&
const DeepCollectionEquality().equals(other.error, error)); const DeepCollectionEquality().equals(other.error, error));
} }
@@ -595,8 +607,8 @@ class _$Failed implements Failed {
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
$FailedCopyWith<Failed> get copyWith => _$$FailedCopyWith<_$Failed> get copyWith =>
_$FailedCopyWithImpl<Failed>(this, _$identity); __$$FailedCopyWithImpl<_$Failed>(this, _$identity);
@override @override
@optionalTypeArgs @optionalTypeArgs
@@ -678,12 +690,14 @@ class _$Failed implements Failed {
} }
} }
abstract class Failed implements UploadState { abstract class Failed extends UploadState {
const factory Failed({required final String error}) = _$Failed; const factory Failed({required final String error}) = _$Failed;
const Failed._() : super._();
factory Failed.fromJson(Map<String, dynamic> json) = _$Failed.fromJson; factory Failed.fromJson(Map<String, dynamic> json) = _$Failed.fromJson;
String get error => throw _privateConstructorUsedError; String get error => throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
$FailedCopyWith<Failed> get copyWith => throw _privateConstructorUsedError; _$$FailedCopyWith<_$Failed> get copyWith =>
throw _privateConstructorUsedError;
} }
@@ -22,9 +22,12 @@ class ChannelModel {
DateTime? updatedAt, DateTime? updatedAt,
this.deletedAt, this.deletedAt,
this.memberCount = 0, this.memberCount = 0,
this.extraData = const {}, Map<String, Object?> extraData = const {},
this.team, this.team,
this.cooldown = 0, this.cooldown = 0,
bool? disabled,
bool? hidden,
DateTime? truncatedAt,
}) : assert( }) : assert(
(cid != null && cid.contains(':')) || (id != null && type != null), (cid != null && cid.contains(':')) || (id != null && type != null),
'provide either a cid or an id and type', 'provide either a cid or an id and type',
@@ -34,7 +37,18 @@ class ChannelModel {
cid = cid ?? '$type:$id', cid = cid ?? '$type:$id',
config = config ?? ChannelConfig(), config = config ?? ChannelConfig(),
createdAt = createdAt ?? DateTime.now(), createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now(); updatedAt = updatedAt ?? DateTime.now(),
// TODO: Make them top-level fields in v5
// For backwards compatibility, set 'disabled', 'hidden'
// and 'truncated_at' in [extraData].
extraData = {
...extraData,
if (disabled != null) 'disabled': disabled,
if (hidden != null) 'hidden': hidden,
if (truncatedAt != null)
'truncated_at': truncatedAt.toIso8601String(),
};
/// Create a new instance from a json /// Create a new instance from a json
factory ChannelModel.fromJson(Map<String, dynamic> json) => factory ChannelModel.fromJson(Map<String, dynamic> json) =>
@@ -92,6 +106,22 @@ class ChannelModel {
@JsonKey(includeIfNull: false) @JsonKey(includeIfNull: false)
final int cooldown; final int cooldown;
/// True if the channel is disabled
@JsonKey(ignore: true)
bool? get disabled => extraData['disabled'] as bool?;
/// True if the channel is hidden
@JsonKey(ignore: true)
bool? get hidden => extraData['hidden'] as bool?;
/// The date of the last time channel got truncated
@JsonKey(ignore: true)
DateTime? get truncatedAt {
final truncatedAt = extraData['truncated_at'] as String?;
if (truncatedAt == null) return null;
return DateTime.parse(truncatedAt);
}
/// Map of custom channel extraData /// Map of custom channel extraData
@JsonKey(includeIfNull: false) @JsonKey(includeIfNull: false)
final Map<String, Object?> extraData; final Map<String, Object?> extraData;
@@ -145,6 +175,9 @@ class ChannelModel {
Map<String, Object?>? extraData, Map<String, Object?>? extraData,
String? team, String? team,
int? cooldown, int? cooldown,
bool? disabled,
bool? hidden,
DateTime? truncatedAt,
}) => }) =>
ChannelModel( ChannelModel(
id: id ?? this.id, id: id ?? this.id,
@@ -162,6 +195,14 @@ class ChannelModel {
extraData: extraData ?? this.extraData, extraData: extraData ?? this.extraData,
team: team ?? this.team, team: team ?? this.team,
cooldown: cooldown ?? this.cooldown, cooldown: cooldown ?? this.cooldown,
disabled: disabled ?? extraData?['disabled'] as bool? ?? this.disabled,
hidden: hidden ?? extraData?['hidden'] as bool? ?? this.hidden,
truncatedAt: truncatedAt ??
(extraData?['truncated_at'] == null
? null
// ignore: cast_nullable_to_non_nullable
: DateTime.parse(extraData?['truncated_at'] as String)) ??
this.truncatedAt,
); );
/// Returns a new [ChannelModel] that is a combination of this channelModel /// Returns a new [ChannelModel] that is a combination of this channelModel
@@ -181,9 +222,12 @@ class ChannelModel {
updatedAt: other.updatedAt, updatedAt: other.updatedAt,
deletedAt: other.deletedAt, deletedAt: other.deletedAt,
memberCount: other.memberCount, memberCount: other.memberCount,
extraData: {...extraData, ...other.extraData}, extraData: other.extraData,
team: other.team, team: other.team,
cooldown: other.cooldown, cooldown: other.cooldown,
disabled: other.disabled,
hidden: other.hidden,
truncatedAt: other.truncatedAt,
); );
} }
} }
@@ -19,6 +19,7 @@ class ChannelState {
this.watcherCount, this.watcherCount,
this.watchers, this.watchers,
this.read, this.read,
this.membership,
}); });
/// The channel to which this state belongs /// The channel to which this state belongs
@@ -42,6 +43,9 @@ class ChannelState {
/// The list of channel reads /// The list of channel reads
final List<Read>? read; final List<Read>? read;
/// Relationship of the current user to this channel.
final Member? membership;
/// Create a new instance from a json /// Create a new instance from a json
static ChannelState fromJson(Map<String, dynamic> json) => static ChannelState fromJson(Map<String, dynamic> json) =>
_$ChannelStateFromJson(json); _$ChannelStateFromJson(json);
@@ -58,6 +62,7 @@ class ChannelState {
int? watcherCount, int? watcherCount,
List<User>? watchers, List<User>? watchers,
List<Read>? read, List<Read>? read,
Member? membership,
}) => }) =>
ChannelState( ChannelState(
channel: channel ?? this.channel, channel: channel ?? this.channel,
@@ -67,5 +72,6 @@ class ChannelState {
watcherCount: watcherCount ?? this.watcherCount, watcherCount: watcherCount ?? this.watcherCount,
watchers: watchers ?? this.watchers, watchers: watchers ?? this.watchers,
read: read ?? this.read, read: read ?? this.read,
membership: membership ?? this.membership,
); );
} }
@@ -26,6 +26,9 @@ ChannelState _$ChannelStateFromJson(Map<String, dynamic> json) => ChannelState(
read: (json['read'] as List<dynamic>?) read: (json['read'] as List<dynamic>?)
?.map((e) => Read.fromJson(e as Map<String, dynamic>)) ?.map((e) => Read.fromJson(e as Map<String, dynamic>))
.toList(), .toList(),
membership: json['membership'] == null
? null
: Member.fromJson(json['membership'] as Map<String, dynamic>),
); );
Map<String, dynamic> _$ChannelStateToJson(ChannelState instance) => Map<String, dynamic> _$ChannelStateToJson(ChannelState instance) =>
@@ -38,4 +41,5 @@ Map<String, dynamic> _$ChannelStateToJson(ChannelState instance) =>
'watcher_count': instance.watcherCount, 'watcher_count': instance.watcherCount,
'watchers': instance.watchers?.map((e) => e.toJson()).toList(), 'watchers': instance.watchers?.map((e) => e.toJson()).toList(),
'read': instance.read?.map((e) => e.toJson()).toList(), 'read': instance.read?.map((e) => e.toJson()).toList(),
'membership': instance.membership?.toJson(),
}; };
@@ -177,36 +177,25 @@ class EventChannel extends ChannelModel {
/// Constructor used for json serialization /// Constructor used for json serialization
EventChannel({ EventChannel({
this.members, this.members,
String? id, super.id,
String? type, super.type,
required String cid, required String super.cid,
required ChannelConfig config, super.ownCapabilities,
User? createdBy, required ChannelConfig super.config,
bool frozen = false, super.createdBy,
DateTime? lastMessageAt, super.frozen,
required DateTime createdAt, super.lastMessageAt,
required DateTime updatedAt, required DateTime super.createdAt,
DateTime? deletedAt, required DateTime super.updatedAt,
int memberCount = 0, super.deletedAt,
super.memberCount,
Map<String, Object?>? extraData, Map<String, Object?>? extraData,
int cooldown = 0, super.cooldown,
String? team, super.team,
}) : super( super.disabled,
id: id, super.hidden,
type: type, super.truncatedAt,
cid: cid, }) : super(extraData: extraData ?? {});
config: config,
createdBy: createdBy,
frozen: frozen,
lastMessageAt: lastMessageAt,
createdAt: createdAt,
updatedAt: updatedAt,
deletedAt: deletedAt,
memberCount: memberCount,
extraData: extraData ?? {},
cooldown: cooldown,
team: team,
);
/// Create a new instance from a json /// Create a new instance from a json
factory EventChannel.fromJson(Map<String, dynamic> json) => factory EventChannel.fromJson(Map<String, dynamic> json) =>
@@ -81,6 +81,9 @@ EventChannel _$EventChannelFromJson(Map<String, dynamic> json) => EventChannel(
id: json['id'] as String?, id: json['id'] as String?,
type: json['type'] as String?, type: json['type'] as String?,
cid: json['cid'] as String, cid: json['cid'] as String,
ownCapabilities: (json['own_capabilities'] as List<dynamic>?)
?.map((e) => e as String)
.toList(),
config: ChannelConfig.fromJson(json['config'] as Map<String, dynamic>), config: ChannelConfig.fromJson(json['config'] as Map<String, dynamic>),
createdBy: json['created_by'] == null createdBy: json['created_by'] == null
? null ? null
@@ -53,29 +53,43 @@ enum FilterOperator {
nor, nor,
/// Matches any list that contains the specified value /// Matches any list that contains the specified value
contains, contains;
}
/// Helper extension for [FilterOperator] @override
extension FilterOperatorX on FilterOperator { String toString() {
/// Converts [FilterOperator] into rew values switch (this) {
String get rawValue => { case FilterOperator.equal:
FilterOperator.equal: '\$eq', return r'$eq';
FilterOperator.notEqual: '\$ne', case FilterOperator.notEqual:
FilterOperator.greater: '\$gt', return r'$ne';
FilterOperator.greaterOrEqual: '\$gte', case FilterOperator.greater:
FilterOperator.less: '\$lt', return r'$gt';
FilterOperator.lessOrEqual: '\$lte', case FilterOperator.greaterOrEqual:
FilterOperator.in_: '\$in', return r'$gte';
FilterOperator.notIn: '\$nin', case FilterOperator.less:
FilterOperator.query: '\$q', return r'$lt';
FilterOperator.autoComplete: '\$autocomplete', case FilterOperator.lessOrEqual:
FilterOperator.exists: '\$exists', return r'$lte';
FilterOperator.and: '\$and', case FilterOperator.in_:
FilterOperator.or: '\$or', return r'$in';
FilterOperator.nor: '\$nor', case FilterOperator.notIn:
FilterOperator.contains: '\$contains', return r'$nin';
}[this]!; case FilterOperator.query:
return r'$q';
case FilterOperator.autoComplete:
return r'$autocomplete';
case FilterOperator.exists:
return r'$exists';
case FilterOperator.and:
return r'$and';
case FilterOperator.or:
return r'$or';
case FilterOperator.nor:
return r'$nor';
case FilterOperator.contains:
return r'$contains';
}
}
} }
/// Stream supports a limited set of filters for querying channels, /// Stream supports a limited set of filters for querying channels,
@@ -96,11 +110,11 @@ class Filter extends Equatable {
this.key, this.key,
}); });
Filter._({ const Filter._({
required FilterOperator operator, required FilterOperator operator,
required this.value, required this.value,
this.key, this.key,
}) : operator = operator.rawValue; }) : operator = '$operator';
/// An empty filter /// An empty filter
const Filter.empty() const Filter.empty()
@@ -215,7 +229,7 @@ class Filter extends Equatable {
/// Serializes to json object /// Serializes to json object
Map<String, Object?> toJson() { Map<String, Object?> toJson() {
final json = <String, Object?>{}; final json = <String, Object?>{};
final groupOperators = _groupOperators.map((it) => it.rawValue); final groupOperators = _groupOperators.map((it) => '$it');
if (groupOperators.contains(operator)) { if (groupOperators.contains(operator)) {
// Filters with group operators are encoded in the following form: // Filters with group operators are encoded in the following form:
@@ -17,34 +17,20 @@ class OwnUser extends User {
this.totalUnreadCount = 0, this.totalUnreadCount = 0,
this.unreadChannels = 0, this.unreadChannels = 0,
this.channelMutes = const [], this.channelMutes = const [],
required String id, required super.id,
String? role, super.role,
String? name, super.name,
String? image, super.image,
DateTime? createdAt, super.createdAt,
DateTime? updatedAt, super.updatedAt,
DateTime? lastActive, super.lastActive,
bool online = false, super.online,
Map<String, Object?> extraData = const {}, super.extraData,
bool banned = false, super.banned,
DateTime? banExpires, super.banExpires,
List<String> teams = const [], super.teams,
String? language, super.language,
}) : super( });
id: id,
role: role,
name: name,
image: image,
createdAt: createdAt,
updatedAt: updatedAt,
lastActive: lastActive,
online: online,
extraData: extraData,
banned: banned,
banExpires: banExpires,
teams: teams,
language: language,
);
/// Create a new instance from json. /// Create a new instance from json.
factory OwnUser.fromJson(Map<String, dynamic> json) => _$OwnUserFromJson( factory OwnUser.fromJson(Map<String, dynamic> json) => _$OwnUserFromJson(
@@ -55,6 +41,9 @@ class OwnUser extends User {
factory OwnUser.fromUser(User user) => OwnUser( factory OwnUser.fromUser(User user) => OwnUser(
id: user.id, id: user.id,
role: user.role, role: user.role,
// Using extraData value in order to not use id as name.
name: user.extraData['name'] as String?,
image: user.image,
createdAt: user.createdAt, createdAt: user.createdAt,
updatedAt: user.updatedAt, updatedAt: user.updatedAt,
lastActive: user.lastActive, lastActive: user.lastActive,
@@ -90,10 +79,11 @@ class OwnUser extends User {
OwnUser( OwnUser(
id: id ?? this.id, id: id ?? this.id,
role: role ?? this.role, role: role ?? this.role,
// if null, it will be retrieved from extraData['name'] name: name ??
name: name, extraData?['name'] as String? ??
// if null, it will be retrieved from extraData['image'] // Using extraData value in order to not use id as name.
image: image, this.extraData['name'] as String?,
image: image ?? extraData?['image'] as String? ?? this.image,
banned: banned ?? this.banned, banned: banned ?? this.banned,
banExpires: banExpires ?? this.banExpires, banExpires: banExpires ?? this.banExpires,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
@@ -117,6 +107,9 @@ class OwnUser extends User {
return copyWith( return copyWith(
id: other.id, id: other.id,
role: other.role, role: other.role,
// Using extraData value in order to not use id as name.
name: other.extraData['name'] as String?,
image: other.image,
banned: other.banned, banned: other.banned,
channelMutes: other.channelMutes, channelMutes: other.channelMutes,
createdAt: other.createdAt, createdAt: other.createdAt,
@@ -46,6 +46,7 @@ class User extends Equatable {
this.language, this.language,
}) : createdAt = createdAt ?? DateTime.now(), }) : createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now(), updatedAt = updatedAt ?? DateTime.now(),
// TODO: Make them top-level fields in v5
// For backwards compatibility, set 'name', 'image' in [extraData]. // For backwards compatibility, set 'name', 'image' in [extraData].
extraData = { extraData = {
...extraData, ...extraData,
@@ -171,10 +172,11 @@ class User extends Equatable {
User( User(
id: id ?? this.id, id: id ?? this.id,
role: role ?? this.role, role: role ?? this.role,
// if null, it will be retrieved from extraData['name'] name: name ??
name: name, extraData?['name'] as String? ??
// if null, it will be retrieved from extraData['image'] // Using extraData value in order to not use id as name.
image: image, this.extraData['name'] as String?,
image: image ?? extraData?['image'] as String? ?? this.image,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
lastActive: lastActive ?? this.lastActive, lastActive: lastActive ?? this.lastActive,
@@ -73,6 +73,9 @@ class EventType {
/// Event sent when a member is removed to a channel /// Event sent when a member is removed to a channel
static const String memberRemoved = 'member.removed'; static const String memberRemoved = 'member.removed';
/// Event sent when a member is updated in a channel
static const String memberUpdated = 'member.updated';
/// Event sent when a member is removed to a channel /// Event sent when a member is removed to a channel
static const String userBanned = 'user.banned'; static const String userBanned = 'user.banned';
@@ -1,29 +0,0 @@
///
enum Location {
///
usEast,
///
euWest,
///
mumbai,
///
sydney,
///
singapore,
}
///
extension LocationX on Location {
///
String get name => {
Location.usEast: 'us-east',
Location.euWest: 'dublin',
Location.mumbai: 'mumbai',
Location.sydney: 'sydney',
Location.singapore: 'singapore',
}[this]!;
}
@@ -6,7 +6,6 @@ import 'package:logging/logging.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/src/core/error/error.dart'; import 'package:stream_chat/src/core/error/error.dart';
import 'package:stream_chat/src/core/http/token.dart';
import 'package:stream_chat/src/core/http/token_manager.dart'; import 'package:stream_chat/src/core/http/token_manager.dart';
import 'package:stream_chat/src/core/models/event.dart'; import 'package:stream_chat/src/core/models/event.dart';
import 'package:stream_chat/src/core/models/user.dart'; import 'package:stream_chat/src/core/models/user.dart';
@@ -163,7 +162,7 @@ class WebSocket with TimerHelper {
'json': jsonEncode(params), 'json': jsonEncode(params),
'api_key': apiKey, 'api_key': apiKey,
'authorization': token.rawValue, 'authorization': token.rawValue,
'stream-auth-type': token.authType.raw, 'stream-auth-type': token.authType.name,
...queryParameters, ...queryParameters,
}; };
final scheme = baseUrl.startsWith('https') ? 'wss' : 'ws'; final scheme = baseUrl.startsWith('https') ? 'wss' : 'ws';
@@ -36,7 +36,6 @@ export './src/core/models/user.dart';
export './src/core/util/extension.dart'; export './src/core/util/extension.dart';
export './src/db/chat_persistence_client.dart'; export './src/db/chat_persistence_client.dart';
export './src/event_type.dart'; export './src/event_type.dart';
export './src/location.dart';
export './src/permission_type.dart'; export './src/permission_type.dart';
export './src/ws/connection_status.dart'; export './src/ws/connection_status.dart';
export 'src/client/channel.dart'; export 'src/client/channel.dart';
@@ -67,5 +66,4 @@ export 'src/core/models/user.dart';
export 'src/core/util/extension.dart'; export 'src/core/util/extension.dart';
export 'src/db/chat_persistence_client.dart'; export 'src/db/chat_persistence_client.dart';
export 'src/event_type.dart'; export 'src/event_type.dart';
export 'src/location.dart';
export 'src/ws/connection_status.dart'; export 'src/ws/connection_status.dart';
+1 -1
View File
@@ -3,4 +3,4 @@ import 'package:stream_chat/src/client/client.dart';
/// Current package version /// Current package version
/// Used in [StreamChatClient] to build the `x-stream-client` header /// Used in [StreamChatClient] to build the `x-stream-client` header
// ignore: constant_identifier_names // ignore: constant_identifier_names
const PACKAGE_VERSION = '4.1.0'; const PACKAGE_VERSION = '4.2.0';
+6 -6
View File
@@ -1,22 +1,22 @@
name: stream_chat name: stream_chat
homepage: https://getstream.io/ homepage: https://getstream.io/
description: The official Dart client for Stream Chat, a service for building chat applications. description: The official Dart client for Stream Chat, a service for building chat applications.
version: 4.1.0 version: 4.2.0
repository: https://github.com/GetStream/stream-chat-flutter repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.17.0 <3.0.0'
dependencies: dependencies:
async: ^2.5.0 async: ^2.5.0
collection: ^1.15.0 collection: ^1.15.0
dio: ^4.0.0 dio: ^4.0.0
equatable: ^2.0.0 equatable: ^2.0.0
freezed_annotation: ^1.0.0 freezed_annotation: ^2.0.3
http_parser: ^4.0.0 http_parser: ^4.0.0
jose: ^0.3.2 jose: ^0.3.2
json_annotation: ^4.3.0 json_annotation: ^4.5.0
logging: ^1.0.1 logging: ^1.0.1
meta: ^1.3.0 meta: ^1.3.0
mime: ^1.0.0 mime: ^1.0.0
@@ -28,7 +28,7 @@ dependencies:
dev_dependencies: dev_dependencies:
build_runner: ^2.0.1 build_runner: ^2.0.1
dart_code_metrics: ^4.4.0 dart_code_metrics: ^4.4.0
freezed: ^1.0.0 freezed: ^2.0.3
json_serializable: ^6.0.1 json_serializable: ^6.2.0
mocktail: ^0.3.0 mocktail: ^0.3.0
test: ^1.17.12 test: ^1.17.12
@@ -11,6 +11,7 @@
}, },
"watchers": [], "watchers": [],
"read": [], "read": [],
"membership": null,
"messages": [ "messages": [
{ {
"id": "dry-meadow-0-2b73cc8b-cd86-4a01-8d40-bd82ad07a030", "id": "dry-meadow-0-2b73cc8b-cd86-4a01-8d40-bd82ad07a030",
@@ -1,5 +1,4 @@
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:stream_chat/src/core/api/device_api.dart';
import 'package:stream_chat/src/core/http/token.dart'; import 'package:stream_chat/src/core/http/token.dart';
import 'package:stream_chat/src/core/models/banned_user.dart'; import 'package:stream_chat/src/core/models/banned_user.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
@@ -2514,7 +2513,7 @@ void main() {
}); });
test( test(
'''setting the `currentUser` should also compute and update the unreadCounts''', 'setting the `currentUser` should also compute and update the unreadCounts',
() { () {
final state = client.state; final state = client.state;
final initialUser = OwnUser.fromUser(user); final initialUser = OwnUser.fromUser(user);
@@ -46,7 +46,7 @@ void main() {
expect(updateHeaders.containsKey('Authorization'), isTrue); expect(updateHeaders.containsKey('Authorization'), isTrue);
expect(updateHeaders['Authorization'], token.rawValue); expect(updateHeaders['Authorization'], token.rawValue);
expect(updateHeaders.containsKey('stream-auth-type'), isTrue); expect(updateHeaders.containsKey('stream-auth-type'), isTrue);
expect(updateHeaders['stream-auth-type'], token.authType.raw); expect(updateHeaders['stream-auth-type'], token.authType.name);
expect(updatedQueryParams.containsKey('user_id'), isTrue); expect(updatedQueryParams.containsKey('user_id'), isTrue);
expect(updatedQueryParams['user_id'], token.userId); expect(updatedQueryParams['user_id'], token.userId);
@@ -10,7 +10,7 @@ void main() {
expect(token.userId, userId); expect(token.userId, userId);
expect(token.rawValue, isEmpty); expect(token.rawValue, isEmpty);
expect(token.authType, AuthType.anonymous); expect(token.authType, AuthType.anonymous);
expect(token.authType.raw, AuthType.anonymous.raw); expect(token.authType.name, AuthType.anonymous.name);
}); });
test('`.fromRawValue` should create token from rawValue', () { test('`.fromRawValue` should create token from rawValue', () {
@@ -36,7 +36,7 @@ void main() {
expect(token.userId, userId); expect(token.userId, userId);
expect(token.rawValue, isNotEmpty); expect(token.rawValue, isNotEmpty);
expect(token.authType, AuthType.jwt); expect(token.authType, AuthType.jwt);
expect(token.authType.raw, AuthType.jwt.raw); expect(token.authType.name, AuthType.jwt.name);
}); });
test( test(
@@ -51,7 +51,7 @@ void main() {
expect(token.userId, user.id); expect(token.userId, user.id);
expect(token.rawValue, isNotEmpty); expect(token.rawValue, isNotEmpty);
expect(token.authType, AuthType.jwt); expect(token.authType, AuthType.jwt);
expect(token.authType.raw, AuthType.jwt.raw); expect(token.authType.name, AuthType.jwt.name);
}, },
); );
} }
@@ -49,6 +49,7 @@ void main() {
channel: ChannelModel.fromJson(j['channel']), channel: ChannelModel.fromJson(j['channel']),
members: [], members: [],
messages: messages:
// ignore: unnecessary_lambdas
(j['messages'] as List).map((m) => Message.fromJson(m)).toList(), (j['messages'] as List).map((m) => Message.fromJson(m)).toList(),
read: [], read: [],
watcherCount: 5, watcherCount: 5,
@@ -55,4 +55,140 @@ void main() {
); );
}); });
}); });
test('hidden property and extraData manipulation', () {
final channel = ChannelModel(cid: 'test:cid', hidden: false);
expect(channel.hidden, false);
expect(channel.extraData['hidden'], false);
print(channel.toJson());
expect(channel.toJson(), {
'id': 'cid',
'type': 'test',
'frozen': false,
'cooldown': 0,
'hidden': false,
});
expect(ChannelModel.fromJson(channel.toJson()).toJson(), {
'id': 'cid',
'type': 'test',
'frozen': false,
'cooldown': 0,
'hidden': false,
});
var newChannel = channel.copyWith(
extraData: {'hidden': true},
);
expect(newChannel.extraData['hidden'], true);
expect(newChannel.hidden, true);
newChannel = channel.copyWith(
hidden: false,
);
expect(newChannel.extraData['hidden'], false);
expect(newChannel.hidden, false);
newChannel = channel.copyWith(
hidden: true,
extraData: {'hidden': true},
);
expect(newChannel.extraData['hidden'], true);
expect(newChannel.hidden, true);
});
test('disabled property and extraData manipulation', () {
final channel = ChannelModel(cid: 'test:cid', disabled: false);
expect(channel.disabled, false);
expect(channel.extraData['disabled'], false);
print(channel.toJson());
expect(channel.toJson(), {
'id': 'cid',
'type': 'test',
'frozen': false,
'cooldown': 0,
'disabled': false,
});
expect(ChannelModel.fromJson(channel.toJson()).toJson(), {
'id': 'cid',
'type': 'test',
'frozen': false,
'cooldown': 0,
'disabled': false,
});
var newChannel = channel.copyWith(
extraData: {'disabled': true},
);
expect(newChannel.extraData['disabled'], true);
expect(newChannel.disabled, true);
newChannel = channel.copyWith(
hidden: false,
);
expect(newChannel.extraData['disabled'], false);
expect(newChannel.disabled, false);
newChannel = channel.copyWith(
hidden: true,
extraData: {'disabled': true},
);
expect(newChannel.extraData['disabled'], true);
expect(newChannel.disabled, true);
});
test('truncatedAt property and extraData manipulation', () {
final currentDate = DateTime.now();
final channel = ChannelModel(cid: 'test:cid', truncatedAt: currentDate);
expect(channel.truncatedAt, currentDate);
expect(channel.extraData['truncated_at'], currentDate.toIso8601String());
print(channel.toJson());
expect(channel.toJson(), {
'id': 'cid',
'type': 'test',
'frozen': false,
'cooldown': 0,
'truncated_at': currentDate.toIso8601String(),
});
expect(ChannelModel.fromJson(channel.toJson()).toJson(), {
'id': 'cid',
'type': 'test',
'frozen': false,
'cooldown': 0,
'truncated_at': currentDate.toIso8601String(),
});
final dateOne = DateTime.now();
var newChannel = channel.copyWith(
extraData: {'truncated_at': dateOne.toIso8601String()},
);
expect(newChannel.extraData['truncated_at'], dateOne.toIso8601String());
expect(newChannel.truncatedAt, dateOne);
final dateTwo = DateTime.now();
newChannel = channel.copyWith(
truncatedAt: dateTwo,
);
expect(newChannel.extraData['truncated_at'], dateTwo.toIso8601String());
expect(newChannel.truncatedAt, dateTwo);
final dateThree = DateTime.now();
newChannel = channel.copyWith(
truncatedAt: dateThree,
extraData: {'truncated_at': dateThree.toIso8601String()},
);
expect(newChannel.extraData['truncated_at'], dateThree.toIso8601String());
expect(newChannel.truncatedAt, dateThree);
});
} }
@@ -11,7 +11,7 @@ void main() {
final filter = Filter.equal(key, value); final filter = Filter.equal(key, value);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, value); expect(filter.value, value);
expect(filter.operator, FilterOperator.equal.rawValue); expect(filter.operator, FilterOperator.equal.toString());
}); });
test('notEqual', () { test('notEqual', () {
@@ -20,7 +20,7 @@ void main() {
final filter = Filter.notEqual(key, value); final filter = Filter.notEqual(key, value);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, value); expect(filter.value, value);
expect(filter.operator, FilterOperator.notEqual.rawValue); expect(filter.operator, FilterOperator.notEqual.toString());
}); });
test('greater', () { test('greater', () {
@@ -29,7 +29,7 @@ void main() {
final filter = Filter.greater(key, value); final filter = Filter.greater(key, value);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, value); expect(filter.value, value);
expect(filter.operator, FilterOperator.greater.rawValue); expect(filter.operator, FilterOperator.greater.toString());
}); });
test('greaterOrEqual', () { test('greaterOrEqual', () {
@@ -38,7 +38,7 @@ void main() {
final filter = Filter.greaterOrEqual(key, value); final filter = Filter.greaterOrEqual(key, value);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, value); expect(filter.value, value);
expect(filter.operator, FilterOperator.greaterOrEqual.rawValue); expect(filter.operator, FilterOperator.greaterOrEqual.toString());
}); });
test('less', () { test('less', () {
@@ -47,7 +47,7 @@ void main() {
final filter = Filter.less(key, value); final filter = Filter.less(key, value);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, value); expect(filter.value, value);
expect(filter.operator, FilterOperator.less.rawValue); expect(filter.operator, FilterOperator.less.toString());
}); });
test('lessOrEqual', () { test('lessOrEqual', () {
@@ -56,7 +56,7 @@ void main() {
final filter = Filter.lessOrEqual(key, value); final filter = Filter.lessOrEqual(key, value);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, value); expect(filter.value, value);
expect(filter.operator, FilterOperator.lessOrEqual.rawValue); expect(filter.operator, FilterOperator.lessOrEqual.toString());
}); });
test('in', () { test('in', () {
@@ -65,7 +65,7 @@ void main() {
final filter = Filter.in_(key, values); final filter = Filter.in_(key, values);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, values); expect(filter.value, values);
expect(filter.operator, FilterOperator.in_.rawValue); expect(filter.operator, FilterOperator.in_.toString());
}); });
test('in', () { test('in', () {
@@ -74,7 +74,7 @@ void main() {
final filter = Filter.in_(key, values); final filter = Filter.in_(key, values);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, values); expect(filter.value, values);
expect(filter.operator, FilterOperator.in_.rawValue); expect(filter.operator, FilterOperator.in_.toString());
}); });
test('notIn', () { test('notIn', () {
@@ -83,7 +83,7 @@ void main() {
final filter = Filter.notIn(key, values); final filter = Filter.notIn(key, values);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, values); expect(filter.value, values);
expect(filter.operator, FilterOperator.notIn.rawValue); expect(filter.operator, FilterOperator.notIn.toString());
}); });
test('query', () { test('query', () {
@@ -92,7 +92,7 @@ void main() {
final filter = Filter.query(key, value); final filter = Filter.query(key, value);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, value); expect(filter.value, value);
expect(filter.operator, FilterOperator.query.rawValue); expect(filter.operator, FilterOperator.query.toString());
}); });
test('autoComplete', () { test('autoComplete', () {
@@ -101,7 +101,7 @@ void main() {
final filter = Filter.autoComplete(key, value); final filter = Filter.autoComplete(key, value);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, value); expect(filter.value, value);
expect(filter.operator, FilterOperator.autoComplete.rawValue); expect(filter.operator, FilterOperator.autoComplete.toString());
}); });
test('exists', () { test('exists', () {
@@ -109,7 +109,7 @@ void main() {
final filter = Filter.exists(key); final filter = Filter.exists(key);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, isTrue); expect(filter.value, isTrue);
expect(filter.operator, FilterOperator.exists.rawValue); expect(filter.operator, FilterOperator.exists.toString());
}); });
test('notExists', () { test('notExists', () {
@@ -117,13 +117,13 @@ void main() {
final filter = Filter.notExists(key); final filter = Filter.notExists(key);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, isFalse); expect(filter.value, isFalse);
expect(filter.operator, FilterOperator.exists.rawValue); expect(filter.operator, FilterOperator.exists.toString());
}); });
test('custom', () { test('custom', () {
const key = 'testKey'; const key = 'testKey';
const value = 'testValue'; const value = 'testValue';
const operator = '\$customOperator'; const operator = r'$customOperator';
const filter = Filter.custom(operator: operator, key: key, value: value); const filter = Filter.custom(operator: operator, key: key, value: value);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, value); expect(filter.value, value);
@@ -149,7 +149,7 @@ void main() {
final filter = Filter.contains(key, values); final filter = Filter.contains(key, values);
expect(filter.key, key); expect(filter.key, key);
expect(filter.value, values); expect(filter.value, values);
expect(filter.operator, FilterOperator.contains.rawValue); expect(filter.operator, FilterOperator.contains.toString());
}); });
group('groupedOperator', () { group('groupedOperator', () {
@@ -161,21 +161,21 @@ void main() {
final filter = Filter.and(filters); final filter = Filter.and(filters);
expect(filter.key, isNull); expect(filter.key, isNull);
expect(filter.value, filters); expect(filter.value, filters);
expect(filter.operator, FilterOperator.and.rawValue); expect(filter.operator, FilterOperator.and.toString());
}); });
test('or', () { test('or', () {
final filter = Filter.or(filters); final filter = Filter.or(filters);
expect(filter.key, isNull); expect(filter.key, isNull);
expect(filter.value, filters); expect(filter.value, filters);
expect(filter.operator, FilterOperator.or.rawValue); expect(filter.operator, FilterOperator.or.toString());
}); });
test('nor', () { test('nor', () {
final filter = Filter.nor(filters); final filter = Filter.nor(filters);
expect(filter.key, isNull); expect(filter.key, isNull);
expect(filter.value, filters); expect(filter.value, filters);
expect(filter.operator, FilterOperator.nor.rawValue); expect(filter.operator, FilterOperator.nor.toString());
}); });
}); });
}); });
@@ -189,7 +189,7 @@ void main() {
final encoded = json.encode(filter); final encoded = json.encode(filter);
expect( expect(
encoded, encoded,
'{"$key":{"${FilterOperator.equal.rawValue}":${json.encode(value)}}}', '''{"$key":{"${FilterOperator.equal.toString()}":${json.encode(value)}}}''',
); );
}); });
test('listValue', () { test('listValue', () {
@@ -199,7 +199,7 @@ void main() {
final encoded = json.encode(filter); final encoded = json.encode(filter);
expect( expect(
encoded, encoded,
'{"$key":{"${FilterOperator.in_.rawValue}":${json.encode(values)}}}', '''{"$key":{"${FilterOperator.in_.toString()}":${json.encode(values)}}}''',
); );
}); });
@@ -243,7 +243,7 @@ void main() {
final encoded = json.encode(filter); final encoded = json.encode(filter);
expect( expect(
encoded, encoded,
'{"${FilterOperator.and.rawValue}":${json.encode(filters)}}', '{"${FilterOperator.and.toString()}":${json.encode(filters)}}',
); );
}); });
@@ -1,3 +1,9 @@
## 4.2.0
🐞 Fixed
- [[#1133]](https://github.com/GetStream/stream-chat-flutter/issues/1133) Visibility override flags not being passed to `StreamMessageActionsModal`
## 4.1.0 ## 4.1.0
✅ Added ✅ Added
@@ -8,7 +14,9 @@
🐞 Fixed 🐞 Fixed
- Fixed attachment picker ui. - Fixed attachment picker ui.
- Fixed StreamChannelHeader and StreamThreadHeader subtitle alignment.
- Fixed message widget thread indicator in reverse mode. - Fixed message widget thread indicator in reverse mode.
- [[#1044]](https://github.com/GetStream/stream-chat-flutter/issues/1044): Refactor StreamMessageWidget bottom row to use Text.rich.
🔄 Changed 🔄 Changed
@@ -47,10 +47,10 @@ class MyApp extends StatelessWidget {
/// If you'd prefer using minimal wrapper widgets for your app, please see /// If you'd prefer using minimal wrapper widgets for your app, please see
/// our other package, `stream_chat_flutter_core`. /// our other package, `stream_chat_flutter_core`.
const MyApp({ const MyApp({
Key? key, super.key,
required this.client, required this.client,
required this.channel, required this.channel,
}) : super(key: key); });
/// Instance of Stream Client. /// Instance of Stream Client.
/// ///
@@ -94,8 +94,8 @@ class MyApp extends StatelessWidget {
class ChannelPage extends StatelessWidget { class ChannelPage extends StatelessWidget {
/// Creates the page that shows the list of messages /// Creates the page that shows the list of messages
const ChannelPage({ const ChannelPage({
Key? key, super.key,
}) : super(key: key); });
@override @override
Widget build(BuildContext context) => Scaffold( Widget build(BuildContext context) => Scaffold(
@@ -22,9 +22,9 @@ void main() async {
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({ const MyApp({
Key? key, super.key,
required this.client, required this.client,
}) : super(key: key); });
final StreamChatClient client; final StreamChatClient client;
@@ -40,8 +40,8 @@ class MyApp extends StatelessWidget {
class SplitView extends StatefulWidget { class SplitView extends StatefulWidget {
const SplitView({ const SplitView({
Key? key, super.key,
}) : super(key: key); });
@override @override
_SplitViewState createState() => _SplitViewState(); _SplitViewState createState() => _SplitViewState();
@@ -86,9 +86,9 @@ class _SplitViewState extends State<SplitView> {
class ChannelListPage extends StatefulWidget { class ChannelListPage extends StatefulWidget {
const ChannelListPage({ const ChannelListPage({
Key? key, super.key,
this.onTap, this.onTap,
}) : super(key: key); });
final void Function(Channel)? onTap; final void Function(Channel)? onTap;
@@ -118,8 +118,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
class ChannelPage extends StatelessWidget { class ChannelPage extends StatelessWidget {
const ChannelPage({ const ChannelPage({
Key? key, super.key,
}) : super(key: key); });
@override @override
Widget build(BuildContext context) => Navigator( Widget build(BuildContext context) => Navigator(
@@ -58,10 +58,10 @@ void main() async {
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({ const MyApp({
Key? key, super.key,
required this.client, required this.client,
required this.channel, required this.channel,
}) : super(key: key); });
final StreamChatClient client; final StreamChatClient client;
@@ -86,8 +86,8 @@ class MyApp extends StatelessWidget {
class ChannelPage extends StatelessWidget { class ChannelPage extends StatelessWidget {
const ChannelPage({ const ChannelPage({
Key? key, super.key,
}) : super(key: key); });
@override @override
Widget build(BuildContext context) => Scaffold( Widget build(BuildContext context) => Scaffold(
@@ -51,9 +51,9 @@ void main() async {
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({ const MyApp({
Key? key, super.key,
required this.client, required this.client,
}) : super(key: key); });
final StreamChatClient client; final StreamChatClient client;
@@ -73,9 +73,9 @@ class MyApp extends StatelessWidget {
class ChannelListPage extends StatefulWidget { class ChannelListPage extends StatefulWidget {
const ChannelListPage({ const ChannelListPage({
Key? key, super.key,
required this.client, required this.client,
}) : super(key: key); });
final StreamChatClient client; final StreamChatClient client;
@@ -121,8 +121,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
class ChannelPage extends StatelessWidget { class ChannelPage extends StatelessWidget {
const ChannelPage({ const ChannelPage({
Key? key, super.key,
}) : super(key: key); });
@override @override
Widget build(BuildContext context) => Scaffold( Widget build(BuildContext context) => Scaffold(
@@ -51,9 +51,9 @@ Future<void> main() async {
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({ const MyApp({
Key? key, super.key,
required this.client, required this.client,
}) : super(key: key); });
final StreamChatClient client; final StreamChatClient client;
@@ -71,8 +71,8 @@ class MyApp extends StatelessWidget {
class ChannelListPage extends StatefulWidget { class ChannelListPage extends StatefulWidget {
const ChannelListPage({ const ChannelListPage({
Key? key, super.key,
}) : super(key: key); });
@override @override
State<ChannelListPage> createState() => _ChannelListPageState(); State<ChannelListPage> createState() => _ChannelListPageState();
@@ -164,8 +164,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
class ChannelPage extends StatelessWidget { class ChannelPage extends StatelessWidget {
const ChannelPage({ const ChannelPage({
Key? key, super.key,
}) : super(key: key); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -38,9 +38,9 @@ Future<void> main() async {
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({ const MyApp({
Key? key, super.key,
required this.client, required this.client,
}) : super(key: key); });
final StreamChatClient client; final StreamChatClient client;
@@ -58,8 +58,8 @@ class MyApp extends StatelessWidget {
class ChannelListPage extends StatefulWidget { class ChannelListPage extends StatefulWidget {
const ChannelListPage({ const ChannelListPage({
Key? key, super.key,
}) : super(key: key); });
@override @override
State<ChannelListPage> createState() => _ChannelListPageState(); State<ChannelListPage> createState() => _ChannelListPageState();
@@ -102,8 +102,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
class ChannelPage extends StatelessWidget { class ChannelPage extends StatelessWidget {
const ChannelPage({ const ChannelPage({
Key? key, super.key,
}) : super(key: key); });
@override @override
Widget build(BuildContext context) => Scaffold( Widget build(BuildContext context) => Scaffold(
@@ -125,9 +125,9 @@ class ChannelPage extends StatelessWidget {
class ThreadPage extends StatelessWidget { class ThreadPage extends StatelessWidget {
const ThreadPage({ const ThreadPage({
Key? key, super.key,
this.parent, this.parent,
}) : super(key: key); });
final Message? parent; final Message? parent;
@@ -41,9 +41,9 @@ Future<void> main() async {
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({ const MyApp({
Key? key, super.key,
required this.client, required this.client,
}) : super(key: key); });
final StreamChatClient client; final StreamChatClient client;
@@ -61,8 +61,8 @@ class MyApp extends StatelessWidget {
class ChannelListPage extends StatefulWidget { class ChannelListPage extends StatefulWidget {
const ChannelListPage({ const ChannelListPage({
Key? key, super.key,
}) : super(key: key); });
@override @override
State<ChannelListPage> createState() => _ChannelListPageState(); State<ChannelListPage> createState() => _ChannelListPageState();
@@ -105,8 +105,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
class ChannelPage extends StatelessWidget { class ChannelPage extends StatelessWidget {
const ChannelPage({ const ChannelPage({
Key? key, super.key,
}) : super(key: key); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -139,7 +139,7 @@ class ChannelPage extends StatelessWidget {
return Padding( return Padding(
padding: const EdgeInsets.all(5), padding: const EdgeInsets.all(5),
child: Container( child: DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( border: Border.all(
color: color, color: color,
@@ -48,9 +48,9 @@ Future<void> main() async {
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({ const MyApp({
Key? key, super.key,
required this.client, required this.client,
}) : super(key: key); });
final StreamChatClient client; final StreamChatClient client;
@@ -101,8 +101,8 @@ class MyApp extends StatelessWidget {
class ChannelListPage extends StatefulWidget { class ChannelListPage extends StatefulWidget {
const ChannelListPage({ const ChannelListPage({
Key? key, super.key,
}) : super(key: key); });
@override @override
State<ChannelListPage> createState() => _ChannelListPageState(); State<ChannelListPage> createState() => _ChannelListPageState();
@@ -145,8 +145,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
class ChannelPage extends StatelessWidget { class ChannelPage extends StatelessWidget {
const ChannelPage({ const ChannelPage({
Key? key, super.key,
}) : super(key: key); });
@override @override
Widget build(BuildContext context) => Scaffold( Widget build(BuildContext context) => Scaffold(
@@ -168,9 +168,9 @@ class ChannelPage extends StatelessWidget {
class ThreadPage extends StatelessWidget { class ThreadPage extends StatelessWidget {
const ThreadPage({ const ThreadPage({
Key? key, super.key,
this.parent, this.parent,
}) : super(key: key); });
final Message? parent; final Message? parent;
@@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.17.0 <3.0.0'
dependencies: dependencies:
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
@@ -320,7 +320,7 @@ class _PositionedListState extends State<PositionedList> {
void _schedulePositionNotificationUpdate() { void _schedulePositionNotificationUpdate() {
if (!updateScheduled) { if (!updateScheduled) {
updateScheduled = true; updateScheduled = true;
SchedulerBinding.instance!.addPostFrameCallback((_) { SchedulerBinding.instance.addPostFrameCallback((_) {
if (registeredElements.value == null) { if (registeredElements.value == null) {
updateScheduled = false; updateScheduled = false;
return; return;
@@ -439,7 +439,7 @@ class _ScrollablePositionedListState extends State<ScrollablePositionedList>
} }
if (_isTransitioning) { if (_isTransitioning) {
_stopScroll(canceled: true); _stopScroll(canceled: true);
SchedulerBinding.instance!.addPostFrameCallback((_) { SchedulerBinding.instance.addPostFrameCallback((_) {
_startScroll( _startScroll(
index: index, index: index,
alignment: alignment, alignment: alignment,
@@ -488,7 +488,7 @@ class _ScrollablePositionedListState extends State<ScrollablePositionedList>
final startCompleter = Completer<void>(); final startCompleter = Completer<void>();
final endCompleter = Completer<void>(); final endCompleter = Completer<void>();
startAnimationCallback = () { startAnimationCallback = () {
SchedulerBinding.instance!.addPostFrameCallback((_) { SchedulerBinding.instance.addPostFrameCallback((_) {
startAnimationCallback = () {}; startAnimationCallback = () {};
opacity.parent = _opacityAnimation(opacityAnimationWeights).animate( opacity.parent = _opacityAnimation(opacityAnimationWeights).animate(
@@ -1,6 +1,5 @@
export 'attachment_upload_state_builder.dart'; export 'attachment_upload_state_builder.dart';
export 'attachment_widget.dart' export 'attachment_widget.dart' show AttachmentError, AttachmentSource;
show AttachmentError, AttachmentSource, AttachmentSourceX;
export 'file_attachment.dart'; export 'file_attachment.dart';
export 'giphy_attachment.dart'; export 'giphy_attachment.dart';
export 'image_attachment.dart'; export 'image_attachment.dart';
@@ -11,10 +11,10 @@ typedef AttachmentTitle = StreamAttachmentTitle;
class StreamAttachmentTitle extends StatelessWidget { class StreamAttachmentTitle extends StatelessWidget {
/// Supply attachment and theme for constructing title /// Supply attachment and theme for constructing title
const StreamAttachmentTitle({ const StreamAttachmentTitle({
Key? key, super.key,
required this.attachment, required this.attachment,
required this.messageTheme, required this.messageTheme,
}) : super(key: key); });
/// Theme to apply to text /// Theme to apply to text
final StreamMessageThemeData messageTheme; final StreamMessageThemeData messageTheme;
@@ -19,14 +19,14 @@ typedef AttachmentUploadStateBuilder = StreamAttachmentUploadStateBuilder;
class StreamAttachmentUploadStateBuilder extends StatelessWidget { class StreamAttachmentUploadStateBuilder extends StatelessWidget {
/// Constructor for creating an [StreamAttachmentUploadStateBuilder] widget /// Constructor for creating an [StreamAttachmentUploadStateBuilder] widget
const StreamAttachmentUploadStateBuilder({ const StreamAttachmentUploadStateBuilder({
Key? key, super.key,
required this.message, required this.message,
required this.attachment, required this.attachment,
this.failedBuilder, this.failedBuilder,
this.successBuilder, this.successBuilder,
this.inProgressBuilder, this.inProgressBuilder,
this.preparingBuilder, this.preparingBuilder,
}) : super(key: key); });
/// Message which attachment is added to /// Message which attachment is added to
final Message message; final Message message;
@@ -85,30 +85,24 @@ class StreamAttachmentUploadStateBuilder extends StatelessWidget {
class _IconButton extends StatelessWidget { class _IconButton extends StatelessWidget {
const _IconButton({ const _IconButton({
Key? key,
this.icon, this.icon,
this.iconSize = 24.0,
this.onPressed, this.onPressed,
this.fillColor, });
}) : super(key: key);
final Widget? icon; final Widget? icon;
final double iconSize;
final VoidCallback? onPressed; final VoidCallback? onPressed;
final Color? fillColor;
@override @override
Widget build(BuildContext context) => SizedBox( Widget build(BuildContext context) => SizedBox(
height: iconSize, height: 24,
width: iconSize, width: 24,
child: RawMaterialButton( child: RawMaterialButton(
elevation: 0, elevation: 0,
highlightElevation: 0, highlightElevation: 0,
focusElevation: 0, focusElevation: 0,
hoverElevation: 0, hoverElevation: 0,
onPressed: onPressed, onPressed: onPressed,
fillColor: fillColor: StreamChatTheme.of(context).colorTheme.overlayDark,
fillColor ?? StreamChatTheme.of(context).colorTheme.overlayDark,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
), ),
@@ -118,10 +112,7 @@ class _IconButton extends StatelessWidget {
} }
class _PreparingState extends StatelessWidget { class _PreparingState extends StatelessWidget {
const _PreparingState({ const _PreparingState({required this.attachmentId});
Key? key,
required this.attachmentId,
}) : super(key: key);
final String attachmentId; final String attachmentId;
@@ -155,11 +146,10 @@ class _PreparingState extends StatelessWidget {
class _InProgressState extends StatelessWidget { class _InProgressState extends StatelessWidget {
const _InProgressState({ const _InProgressState({
Key? key,
required this.sent, required this.sent,
required this.total, required this.total,
required this.attachmentId, required this.attachmentId,
}) : super(key: key); });
final int sent; final int sent;
final int total; final int total;
@@ -195,11 +185,10 @@ class _InProgressState extends StatelessWidget {
class _FailedState extends StatelessWidget { class _FailedState extends StatelessWidget {
const _FailedState({ const _FailedState({
Key? key,
this.error, this.error,
required this.messageId, required this.messageId,
required this.attachmentId, required this.attachmentId,
}) : super(key: key); });
final String? error; final String? error;
final String messageId; final String messageId;
@@ -222,7 +211,7 @@ class _FailedState extends StatelessWidget {
}, },
), ),
Center( Center(
child: Container( child: DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
color: theme.colorTheme.overlayDark.withOpacity(0.6), color: theme.colorTheme.overlayDark.withOpacity(0.6),
@@ -7,14 +7,10 @@ enum AttachmentSource {
local, local,
/// Attachment is uploaded /// Attachment is uploaded
network, network;
}
/// Extension for identifying type of attachment
extension AttachmentSourceX on AttachmentSource {
/// The [when] method is the equivalent to pattern matching. /// The [when] method is the equivalent to pattern matching.
/// Its prototype depends on the AttachmentSource defined. /// Its prototype depends on the AttachmentSource defined.
// ignore: missing_return
T when<T>({ T when<T>({
required T Function() local, required T Function() local,
required T Function() network, required T Function() network,
@@ -38,13 +34,12 @@ typedef AttachmentWidget = StreamAttachmentWidget;
abstract class StreamAttachmentWidget extends StatelessWidget { abstract class StreamAttachmentWidget extends StatelessWidget {
/// Constructor for creating attachment widget /// Constructor for creating attachment widget
const StreamAttachmentWidget({ const StreamAttachmentWidget({
Key? key, super.key,
required this.message, required this.message,
required this.attachment, required this.attachment,
this.size, this.size,
AttachmentSource? source, AttachmentSource? source,
}) : _source = source, }) : _source = source;
super(key: key);
/// Size of attachments /// Size of attachments
final Size? size; final Size? size;
@@ -68,9 +63,9 @@ abstract class StreamAttachmentWidget extends StatelessWidget {
class AttachmentError extends StatelessWidget { class AttachmentError extends StatelessWidget {
/// Constructor for creating AttachmentError /// Constructor for creating AttachmentError
const AttachmentError({ const AttachmentError({
Key? key, super.key,
this.size, this.size,
}) : super(key: key); });
/// Size of error /// Size of error
final Size? size; final Size? size;
@@ -20,19 +20,14 @@ typedef FileAttachment = StreamFileAttachment;
class StreamFileAttachment extends StreamAttachmentWidget { class StreamFileAttachment extends StreamAttachmentWidget {
/// Constructor for creating a widget when attachment is of type 'file' /// Constructor for creating a widget when attachment is of type 'file'
const StreamFileAttachment({ const StreamFileAttachment({
Key? key, super.key,
required Message message, required super.message,
required Attachment attachment, required super.attachment,
Size? size, super.size,
this.title, this.title,
this.trailing, this.trailing,
this.onAttachmentTap, this.onAttachmentTap,
}) : super( });
key: key,
message: message,
attachment: attachment,
size: size,
);
/// Title for attachment /// Title for attachment
final Widget? title; final Widget? title;
@@ -15,19 +15,14 @@ typedef GiphyAttachment = StreamGiphyAttachment;
class StreamGiphyAttachment extends StreamAttachmentWidget { class StreamGiphyAttachment extends StreamAttachmentWidget {
/// Constructor for creating a [StreamGiphyAttachment] widget /// Constructor for creating a [StreamGiphyAttachment] widget
const StreamGiphyAttachment({ const StreamGiphyAttachment({
Key? key, super.key,
required Message message, required super.message,
required Attachment attachment, required super.attachment,
Size? size, super.size,
this.onShowMessage, this.onShowMessage,
this.onReturnAction, this.onReturnAction,
this.onAttachmentTap, this.onAttachmentTap,
}) : super( });
key: key,
message: message,
attachment: attachment,
size: size,
);
/// Callback when show message is tapped /// Callback when show message is tapped
final ShowMessageCallback? onShowMessage; final ShowMessageCallback? onShowMessage;
@@ -15,21 +15,16 @@ typedef ImageAttachment = StreamImageAttachment;
class StreamImageAttachment extends StreamAttachmentWidget { class StreamImageAttachment extends StreamAttachmentWidget {
/// Constructor for creating a [StreamImageAttachment] widget /// Constructor for creating a [StreamImageAttachment] widget
const StreamImageAttachment({ const StreamImageAttachment({
Key? key, super.key,
required Message message, required super.message,
required Attachment attachment, required super.attachment,
required this.messageTheme, required this.messageTheme,
Size? size, super.size,
this.showTitle = false, this.showTitle = false,
this.onShowMessage, this.onShowMessage,
this.onReturnAction, this.onReturnAction,
this.onAttachmentTap, this.onAttachmentTap,
}) : super( });
key: key,
message: message,
attachment: attachment,
size: size,
);
/// [StreamMessageThemeData] for showing image title /// [StreamMessageThemeData] for showing image title
final StreamMessageThemeData messageTheme; final StreamMessageThemeData messageTheme;
@@ -12,7 +12,7 @@ typedef UrlAttachment = StreamUrlAttachment;
class StreamUrlAttachment extends StatelessWidget { class StreamUrlAttachment extends StatelessWidget {
/// Constructor for creating a [StreamUrlAttachment] /// Constructor for creating a [StreamUrlAttachment]
const StreamUrlAttachment({ const StreamUrlAttachment({
Key? key, super.key,
required this.urlAttachment, required this.urlAttachment,
required this.hostDisplayName, required this.hostDisplayName,
required this.messageTheme, required this.messageTheme,
@@ -21,7 +21,7 @@ class StreamUrlAttachment extends StatelessWidget {
vertical: 8, vertical: 8,
), ),
this.onLinkTap, this.onLinkTap,
}) : super(key: key); });
/// Attachment to be displayed /// Attachment to be displayed
final Attachment urlAttachment; final Attachment urlAttachment;
@@ -70,7 +70,7 @@ class StreamUrlAttachment extends StatelessWidget {
Positioned( Positioned(
left: 0, left: 0,
bottom: -1, bottom: -1,
child: Container( child: DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: const BorderRadius.only( borderRadius: const BorderRadius.only(
topRight: Radius.circular(16), topRight: Radius.circular(16),
@@ -14,20 +14,15 @@ typedef VideoAttachment = StreamVideoAttachment;
class StreamVideoAttachment extends StreamAttachmentWidget { class StreamVideoAttachment extends StreamAttachmentWidget {
/// Constructor for creating a [StreamVideoAttachment] widget /// Constructor for creating a [StreamVideoAttachment] widget
const StreamVideoAttachment({ const StreamVideoAttachment({
Key? key, super.key,
required Message message, required super.message,
required Attachment attachment, required super.attachment,
required this.messageTheme, required this.messageTheme,
Size? size, super.size,
this.onShowMessage, this.onShowMessage,
this.onReturnAction, this.onReturnAction,
this.onAttachmentTap, this.onAttachmentTap,
}) : super( });
key: key,
message: message,
attachment: attachment,
size: size,
);
/// [StreamMessageThemeData] for showing title /// [StreamMessageThemeData] for showing title
final StreamMessageThemeData messageTheme; final StreamMessageThemeData messageTheme;
@@ -19,7 +19,7 @@ typedef DownloadedPathCallback = void Function(String? path);
class AttachmentActionsModal extends StatelessWidget { class AttachmentActionsModal extends StatelessWidget {
/// Returns a new [AttachmentActionsModal] /// Returns a new [AttachmentActionsModal]
const AttachmentActionsModal({ const AttachmentActionsModal({
Key? key, super.key,
required this.attachment, required this.attachment,
required this.message, required this.message,
this.onShowMessage, this.onShowMessage,
@@ -30,7 +30,7 @@ class AttachmentActionsModal extends StatelessWidget {
this.showSave = true, this.showSave = true,
this.showDelete = true, this.showDelete = true,
this.customActions = const [], this.customActions = const [],
}) : super(key: key); });
/// The attachment object for which the actions are to be performed /// The attachment object for which the actions are to be performed
final Attachment attachment; final Attachment attachment;
@@ -6,11 +6,11 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
class StreamBackButton extends StatelessWidget { class StreamBackButton extends StatelessWidget {
/// Constructor for creating back button /// Constructor for creating back button
const StreamBackButton({ const StreamBackButton({
Key? key, super.key,
this.onPressed, this.onPressed,
this.showUnreads = false, this.showUnreads = false,
this.cid, this.cid,
}) : super(key: key); });
/// Callback for when button is pressed /// Callback for when button is pressed
final VoidCallback? onPressed; final VoidCallback? onPressed;
@@ -52,7 +52,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
class ChannelAvatar extends StatelessWidget { class ChannelAvatar extends StatelessWidget {
/// Instantiate a new ChannelImage /// Instantiate a new ChannelImage
const ChannelAvatar({ const ChannelAvatar({
Key? key, super.key,
this.channel, this.channel,
this.constraints, this.constraints,
this.onTap, this.onTap,
@@ -60,7 +60,7 @@ class ChannelAvatar extends StatelessWidget {
this.selected = false, this.selected = false,
this.selectionColor, this.selectionColor,
this.selectionThickness = 4, this.selectionThickness = 4,
}) : super(key: key); });
/// [BorderRadius] to display the widget /// [BorderRadius] to display the widget
final BorderRadius? borderRadius; final BorderRadius? borderRadius;
@@ -7,7 +7,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
@Deprecated("Use 'StreamChannelInfoBottomSheet' instead") @Deprecated("Use 'StreamChannelInfoBottomSheet' instead")
class ChannelBottomSheet extends StatefulWidget { class ChannelBottomSheet extends StatefulWidget {
/// Constructor for creating bottom sheet /// Constructor for creating bottom sheet
const ChannelBottomSheet({Key? key, this.onViewInfoTap}) : super(key: key); const ChannelBottomSheet({super.key, this.onViewInfoTap});
/// Callback when 'View Info' is tapped /// Callback when 'View Info' is tapped
final VoidCallback? onViewInfoTap; final VoidCallback? onViewInfoTap;
@@ -60,7 +60,7 @@ class StreamChannelHeader extends StatelessWidget
implements PreferredSizeWidget { implements PreferredSizeWidget {
/// Creates a channel header /// Creates a channel header
const StreamChannelHeader({ const StreamChannelHeader({
Key? key, super.key,
this.showBackButton = true, this.showBackButton = true,
this.onBackPressed, this.onBackPressed,
this.onTitleTap, this.onTitleTap,
@@ -74,8 +74,7 @@ class StreamChannelHeader extends StatelessWidget
this.actions, this.actions,
this.backgroundColor, this.backgroundColor,
this.elevation = 1, this.elevation = 1,
}) : preferredSize = const Size.fromHeight(kToolbarHeight), }) : preferredSize = const Size.fromHeight(kToolbarHeight);
super(key: key);
/// True if this header shows the leading back button /// True if this header shows the leading back button
final bool showBackButton; final bool showBackButton;
@@ -13,12 +13,12 @@ typedef ChannelInfo = StreamChannelInfo;
class StreamChannelInfo extends StatelessWidget { class StreamChannelInfo extends StatelessWidget {
/// Constructor which creates a [StreamChannelInfo] widget /// Constructor which creates a [StreamChannelInfo] widget
const StreamChannelInfo({ const StreamChannelInfo({
Key? key, super.key,
required this.channel, required this.channel,
this.textStyle, this.textStyle,
this.showTypingIndicator = true, this.showTypingIndicator = true,
this.parentId, this.parentId,
}) : super(key: key); });
/// The channel about which the info is to be displayed /// The channel about which the info is to be displayed
final Channel channel; final Channel channel;
@@ -100,12 +100,10 @@ class StreamChannelInfo extends StatelessWidget {
return alternativeWidget ?? const Offstage(); return alternativeWidget ?? const Offstage();
} }
return Align( return StreamTypingIndicator(
child: StreamTypingIndicator( parentId: parentId,
parentId: parentId, style: textStyle,
style: textStyle, alternativeWidget: alternativeWidget,
alternativeWidget: alternativeWidget,
),
); );
} }
@@ -140,7 +138,7 @@ class StreamChannelInfo extends StatelessWidget {
), ),
TextButton( TextButton(
style: TextButton.styleFrom( style: TextButton.styleFrom(
padding: const EdgeInsets.all(0), padding: EdgeInsets.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap, tapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: const VisualDensity( visualDensity: const VisualDensity(
horizontal: VisualDensity.minimumDensity, horizontal: VisualDensity.minimumDensity,
@@ -54,7 +54,7 @@ class StreamChannelListHeader extends StatelessWidget
implements PreferredSizeWidget { implements PreferredSizeWidget {
/// Instantiates a ChannelListHeader /// Instantiates a ChannelListHeader
const StreamChannelListHeader({ const StreamChannelListHeader({
Key? key, super.key,
this.client, this.client,
this.titleBuilder, this.titleBuilder,
this.onUserAvatarTap, this.onUserAvatarTap,
@@ -67,7 +67,7 @@ class StreamChannelListHeader extends StatelessWidget
this.actions, this.actions,
this.backgroundColor, this.backgroundColor,
this.elevation = 1, this.elevation = 1,
}) : super(key: key); });
/// Pass this if you don't have a [StreamChatClient] in your widget tree. /// Pass this if you don't have a [StreamChatClient] in your widget tree.
final StreamChatClient? client; final StreamChatClient? client;
@@ -155,9 +155,7 @@ class StreamChannelListHeader extends StatelessWidget
showOnlineStatus: false, showOnlineStatus: false,
onTap: onUserAvatarTap ?? onTap: onUserAvatarTap ??
(_) { (_) {
if (preNavigationCallback != null) { preNavigationCallback?.call();
preNavigationCallback!();
}
Scaffold.of(context).openDrawer(); Scaffold.of(context).openDrawer();
}, },
borderRadius: channelListHeaderThemeData borderRadius: channelListHeaderThemeData
@@ -57,8 +57,9 @@ typedef ViewInfoCallback = void Function(Channel);
@Deprecated("Use 'StreamChannelListView' instead") @Deprecated("Use 'StreamChannelListView' instead")
class ChannelListView extends StatefulWidget { class ChannelListView extends StatefulWidget {
/// Instantiate a new ChannelListView /// Instantiate a new ChannelListView
@Deprecated("Use 'StreamChannelListView' instead")
ChannelListView({ ChannelListView({
Key? key, super.key,
this.filter, this.filter,
this.sort, this.sort,
this.state = true, this.state = true,
@@ -93,8 +94,7 @@ class ChannelListView extends StatefulWidget {
this.onDeletePressed, this.onDeletePressed,
this.swipeActions, this.swipeActions,
this.channelListController, this.channelListController,
}) : limit = limit ?? pagination?.limit ?? 25, }) : limit = limit ?? pagination?.limit ?? 25;
super(key: key);
/// If true a default swipe to action behaviour will be added to this widget /// If true a default swipe to action behaviour will be added to this widget
final bool swipeToAction; final bool swipeToAction;
@@ -688,7 +688,7 @@ class _ChannelListViewState extends State<ChannelListView> {
initialData: false, initialData: false,
errorBuilder: (context, err) { errorBuilder: (context, err) {
final theme = StreamChatTheme.of(context); final theme = StreamChatTheme.of(context);
return Container( return ColoredBox(
color: theme.colorTheme.textLowEmphasis.withOpacity(0.9), color: theme.colorTheme.textLowEmphasis.withOpacity(0.9),
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
@@ -10,10 +10,10 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
class ChannelName extends StatelessWidget { class ChannelName extends StatelessWidget {
/// Instantiate a new ChannelName /// Instantiate a new ChannelName
const ChannelName({ const ChannelName({
Key? key, super.key,
this.textStyle, this.textStyle,
this.textOverflow = TextOverflow.ellipsis, this.textOverflow = TextOverflow.ellipsis,
}) : super(key: key); });
/// The style of the text displayed /// The style of the text displayed
final TextStyle? textStyle; final TextStyle? textStyle;
@@ -25,7 +25,7 @@ class ChannelPreview extends StatelessWidget {
/// Constructor for creating [ChannelPreview] /// Constructor for creating [ChannelPreview]
const ChannelPreview({ const ChannelPreview({
required this.channel, required this.channel,
Key? key, super.key,
this.onTap, this.onTap,
this.onLongPress, this.onLongPress,
this.onImageTap, this.onImageTap,
@@ -34,7 +34,7 @@ class ChannelPreview extends StatelessWidget {
this.leading, this.leading,
this.sendingIndicator, this.sendingIndicator,
this.trailing, this.trailing,
}) : super(key: key); });
/// Function called when tapping this widget /// Function called when tapping this widget
final void Function(Channel)? onTap; final void Function(Channel)? onTap;
@@ -17,8 +17,8 @@ class StreamCommandsOverlay extends StatelessWidget {
required this.onCommandResult, required this.onCommandResult,
required this.size, required this.size,
required this.channel, required this.channel,
Key? key, super.key,
}) : super(key: key); });
/// The size of the overlay /// The size of the overlay
final Size size; final Size size;
@@ -60,7 +60,7 @@ class StreamCommandsOverlay extends StatelessWidget {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: ListView( child: ListView(
padding: const EdgeInsets.all(0), padding: EdgeInsets.zero,
shrinkWrap: true, shrinkWrap: true,
children: [ children: [
if (commands.isNotEmpty) if (commands.isNotEmpty)
@@ -15,12 +15,12 @@ typedef ConnectionStatusBuilder = StreamConnectionStatusBuilder;
class StreamConnectionStatusBuilder extends StatelessWidget { class StreamConnectionStatusBuilder extends StatelessWidget {
/// Creates a new ConnectionStatusBuilder /// Creates a new ConnectionStatusBuilder
const StreamConnectionStatusBuilder({ const StreamConnectionStatusBuilder({
Key? key, super.key,
required this.statusBuilder, required this.statusBuilder,
this.connectionStatusStream, this.connectionStatusStream,
this.errorBuilder, this.errorBuilder,
this.loadingBuilder, this.loadingBuilder,
}) : super(key: key); });
/// The asynchronous computation to which this builder is currently connected. /// The asynchronous computation to which this builder is currently connected.
final Stream<ConnectionStatus>? connectionStatusStream; final Stream<ConnectionStatus>? connectionStatusStream;
@@ -13,10 +13,10 @@ typedef DateDivider = StreamDateDivider;
class StreamDateDivider extends StatelessWidget { class StreamDateDivider extends StatelessWidget {
/// Constructor for creating a [StreamDateDivider] /// Constructor for creating a [StreamDateDivider]
const StreamDateDivider({ const StreamDateDivider({
Key? key, super.key,
required this.dateTime, required this.dateTime,
this.uppercase = false, this.uppercase = false,
}) : super(key: key); });
/// [DateTime] to display /// [DateTime] to display
final DateTime dateTime; final DateTime dateTime;
@@ -13,13 +13,13 @@ typedef DeletedMessage = StreamDeletedMessage;
class StreamDeletedMessage extends StatelessWidget { class StreamDeletedMessage extends StatelessWidget {
/// Constructor to create [StreamDeletedMessage] /// Constructor to create [StreamDeletedMessage]
const StreamDeletedMessage({ const StreamDeletedMessage({
Key? key, super.key,
required this.messageTheme, required this.messageTheme,
this.borderRadiusGeometry, this.borderRadiusGeometry,
this.shape, this.shape,
this.borderSide, this.borderSide,
this.reverse = false, this.reverse = false,
}) : super(key: key); });
/// The theme of the message /// The theme of the message
final StreamMessageThemeData messageTheme; final StreamMessageThemeData messageTheme;
@@ -17,8 +17,8 @@ class StreamEmojiOverlay extends StatelessWidget {
required this.query, required this.query,
required this.onEmojiResult, required this.onEmojiResult,
required this.size, required this.size,
Key? key, super.key,
}) : super(key: key); });
/// The size of the overlay /// The size of the overlay
final Size size; final Size size;
@@ -65,7 +65,7 @@ class StreamEmojiOverlay extends StatelessWidget {
color: _streamChatTheme.colorTheme.barsBg, color: _streamChatTheme.colorTheme.barsBg,
), ),
child: ListView.builder( child: ListView.builder(
padding: const EdgeInsets.all(0), padding: EdgeInsets.zero,
shrinkWrap: true, shrinkWrap: true,
itemCount: emojis.length + 1, itemCount: emojis.length + 1,
itemBuilder: (context, i) { itemBuilder: (context, i) {
@@ -31,15 +31,14 @@ typedef FullScreenMedia = StreamFullScreenMedia;
class StreamFullScreenMedia extends StatefulWidget { class StreamFullScreenMedia extends StatefulWidget {
/// Instantiate a new FullScreenImage /// Instantiate a new FullScreenImage
const StreamFullScreenMedia({ const StreamFullScreenMedia({
Key? key, super.key,
required this.mediaAttachmentPackages, required this.mediaAttachmentPackages,
this.startIndex = 0, this.startIndex = 0,
String? userName, String? userName,
this.onShowMessage, this.onShowMessage,
this.attachmentActionsModalBuilder, this.attachmentActionsModalBuilder,
this.autoplayVideos = false, this.autoplayVideos = false,
}) : userName = userName ?? '', }) : userName = userName ?? '';
super(key: key);
/// The url of the image /// The url of the image
final List<StreamAttachmentPackage> mediaAttachmentPackages; final List<StreamAttachmentPackage> mediaAttachmentPackages;
@@ -20,7 +20,7 @@ class StreamGalleryFooter extends StatefulWidget
implements PreferredSizeWidget { implements PreferredSizeWidget {
/// Creates a StreamGalleryFooter /// Creates a StreamGalleryFooter
const StreamGalleryFooter({ const StreamGalleryFooter({
Key? key, super.key,
this.onBackPressed, this.onBackPressed,
this.onTitleTap, this.onTitleTap,
this.onImageTap, this.onImageTap,
@@ -29,8 +29,7 @@ class StreamGalleryFooter extends StatefulWidget
required this.mediaAttachmentPackages, required this.mediaAttachmentPackages,
this.mediaSelectedCallBack, this.mediaSelectedCallBack,
this.backgroundColor, this.backgroundColor,
}) : preferredSize = const Size.fromHeight(kToolbarHeight), }) : preferredSize = const Size.fromHeight(kToolbarHeight);
super(key: key);
/// Callback to call when pressing the back button. /// Callback to call when pressing the back button.
/// By default it calls [Navigator.pop] /// By default it calls [Navigator.pop]
@@ -26,7 +26,7 @@ class StreamGalleryHeader extends StatelessWidget
implements PreferredSizeWidget { implements PreferredSizeWidget {
/// Creates a channel header /// Creates a channel header
const StreamGalleryHeader({ const StreamGalleryHeader({
Key? key, super.key,
required this.message, required this.message,
required this.attachment, required this.attachment,
this.showBackButton = true, this.showBackButton = true,
@@ -38,8 +38,7 @@ class StreamGalleryHeader extends StatelessWidget
this.sentAt = '', this.sentAt = '',
this.backgroundColor, this.backgroundColor,
this.attachmentActionsModalBuilder, this.attachmentActionsModalBuilder,
}) : preferredSize = const Size.fromHeight(kToolbarHeight), }) : preferredSize = const Size.fromHeight(kToolbarHeight);
super(key: key);
/// True if this header shows the leading back button /// True if this header shows the leading back button
final bool showBackButton; final bool showBackButton;
@@ -13,10 +13,10 @@ typedef GradientAvatar = StreamGradientAvatar;
class StreamGradientAvatar extends StatefulWidget { class StreamGradientAvatar extends StatefulWidget {
/// Constructor for [StreamGradientAvatar] /// Constructor for [StreamGradientAvatar]
const StreamGradientAvatar({ const StreamGradientAvatar({
Key? key, super.key,
required this.name, required this.name,
required this.userId, required this.userId,
}) : super(key: key); });
/// Name of user to shorten and display /// Name of user to shorten and display
final String name; final String name;
@@ -11,7 +11,7 @@ typedef GroupAvatar = StreamGroupAvatar;
class StreamGroupAvatar extends StatelessWidget { class StreamGroupAvatar extends StatelessWidget {
/// Constructor for creating a [StreamGroupAvatar] /// Constructor for creating a [StreamGroupAvatar]
const StreamGroupAvatar({ const StreamGroupAvatar({
Key? key, super.key,
this.channel, this.channel,
required this.members, required this.members,
this.constraints, this.constraints,
@@ -20,7 +20,7 @@ class StreamGroupAvatar extends StatelessWidget {
this.selected = false, this.selected = false,
this.selectionColor, this.selectionColor,
this.selectionThickness = 4, this.selectionThickness = 4,
}) : super(key: key); });
/// The channel of the avatar /// The channel of the avatar
final Channel? channel; final Channel? channel;
@@ -11,7 +11,7 @@ typedef ImageGroup = StreamImageGroup;
class StreamImageGroup extends StatelessWidget { class StreamImageGroup extends StatelessWidget {
/// Constructor for creating [StreamImageGroup] widget /// Constructor for creating [StreamImageGroup] widget
const StreamImageGroup({ const StreamImageGroup({
Key? key, super.key,
required this.images, required this.images,
required this.message, required this.message,
required this.messageTheme, required this.messageTheme,
@@ -19,7 +19,7 @@ class StreamImageGroup extends StatelessWidget {
this.onReturnAction, this.onReturnAction,
this.onShowMessage, this.onShowMessage,
this.onAttachmentTap, this.onAttachmentTap,
}) : super(key: key); });
/// List of attachments to show /// List of attachments to show
final List<Attachment> images; final List<Attachment> images;
@@ -12,7 +12,7 @@ typedef InfoTile = StreamInfoTile;
class StreamInfoTile extends StatelessWidget { class StreamInfoTile extends StatelessWidget {
/// Constructor for creating an [StreamInfoTile] widget /// Constructor for creating an [StreamInfoTile] widget
const StreamInfoTile({ const StreamInfoTile({
Key? key, super.key,
required this.message, required this.message,
required this.child, required this.child,
required this.showMessage, required this.showMessage,
@@ -20,7 +20,7 @@ class StreamInfoTile extends StatelessWidget {
this.childAnchor, this.childAnchor,
this.textStyle, this.textStyle,
this.backgroundColor, this.backgroundColor,
}) : super(key: key); });
/// String to display /// String to display
final String message; final String message;
@@ -391,7 +391,7 @@ class DefaultTranslations implements Translations {
@override @override
String get sendMessagePermissionError => String get sendMessagePermissionError =>
'You don\'t have permission to send messages'; "You don't have permission to send messages";
@override @override
String get emptyMessagesText => 'There are no messages currently'; String get emptyMessagesText => 'There are no messages currently';
@@ -528,7 +528,7 @@ class DefaultTranslations implements Translations {
@override @override
String get operationCouldNotBeCompletedText => String get operationCouldNotBeCompletedText =>
'The operation couldn\'t be completed.'; "The operation couldn't be completed.";
@override @override
String get replyLabel => 'Reply'; String get replyLabel => 'Reply';
@@ -18,11 +18,11 @@ typedef MediaListView = StreamMediaListView;
class StreamMediaListView extends StatefulWidget { class StreamMediaListView extends StatefulWidget {
/// Constructor for creating a [StreamMediaListView] widget /// Constructor for creating a [StreamMediaListView] widget
const StreamMediaListView({ const StreamMediaListView({
Key? key, super.key,
this.selectedIds = const [], this.selectedIds = const [],
this.onSelect, this.onSelect,
this.controller, this.controller,
}) : super(key: key); });
/// Stores the media selected /// Stores the media selected
final List<String> selectedIds; final List<String> selectedIds;
@@ -67,11 +67,9 @@ class _StreamMediaListViewState extends State<StreamMediaListView> {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 1, vertical: 1), padding: const EdgeInsets.symmetric(horizontal: 1, vertical: 1),
child: InkWell( child: InkWell(
onTap: () { onTap: widget.onSelect == null
if (widget.onSelect != null) { ? null
widget.onSelect!(media); : () => widget.onSelect!(media),
}
},
child: Stack( child: Stack(
children: [ children: [
AspectRatio( AspectRatio(
@@ -13,7 +13,7 @@ typedef MessageActionsModal = StreamMessageActionsModal;
class StreamMessageActionsModal extends StatefulWidget { class StreamMessageActionsModal extends StatefulWidget {
/// Constructor for creating a [StreamMessageActionsModal] widget /// Constructor for creating a [StreamMessageActionsModal] widget
const StreamMessageActionsModal({ const StreamMessageActionsModal({
Key? key, super.key,
required this.message, required this.message,
required this.messageWidget, required this.messageWidget,
required this.messageTheme, required this.messageTheme,
@@ -32,7 +32,7 @@ class StreamMessageActionsModal extends StatefulWidget {
this.reverse = false, this.reverse = false,
this.customActions = const [], this.customActions = const [],
this.onCopyTap, this.onCopyTap,
}) : super(key: key); });
/// Widget that shows the message /// Widget that shows the message
final Widget messageWidget; final Widget messageWidget;
@@ -252,7 +252,7 @@ class _StreamMessageActionsModalState extends State<StreamMessageActionsModal> {
sigmaX: 10, sigmaX: 10,
sigmaY: 10, sigmaY: 10,
), ),
child: Container( child: ColoredBox(
color: streamChatThemeData.colorTheme.overlay, color: streamChatThemeData.colorTheme.overlay,
), ),
), ),
@@ -406,9 +406,7 @@ class _StreamMessageActionsModalState extends State<StreamMessageActionsModal> {
return InkWell( return InkWell(
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
if (widget.onReplyTap != null) { widget.onReplyTap?.call(widget.message);
widget.onReplyTap!(widget.message);
}
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
@@ -660,9 +658,7 @@ class _StreamMessageActionsModalState extends State<StreamMessageActionsModal> {
return InkWell( return InkWell(
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
if (widget.onThreadReplyTap != null) { widget.onThreadReplyTap?.call(widget.message);
widget.onThreadReplyTap!(widget.message);
}
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
@@ -163,7 +163,7 @@ const _kDefaultMaxAttachmentSize = 20971520; // 20MB in Bytes
class MessageInput extends StatefulWidget { class MessageInput extends StatefulWidget {
/// Instantiate a new MessageInput /// Instantiate a new MessageInput
const MessageInput({ const MessageInput({
Key? key, super.key,
this.onMessageSent, this.onMessageSent,
this.preMessageSending, this.preMessageSending,
this.parentMessage, this.parentMessage,
@@ -197,11 +197,10 @@ class MessageInput extends StatefulWidget {
this.customOverlays = const [], this.customOverlays = const [],
this.mentionAllAppUsers = false, this.mentionAllAppUsers = false,
this.shouldKeepFocusAfterMessage, this.shouldKeepFocusAfterMessage,
}) : assert( }) : assert(
initialMessage == null || editMessage == null, initialMessage == null || editMessage == null,
"Can't provide both `initialMessage` and `editMessage`", "Can't provide both `initialMessage` and `editMessage`",
), );
super(key: key);
/// List of options for showing overlays /// List of options for showing overlays
final List<OverlayOptions> customOverlays; final List<OverlayOptions> customOverlays;
@@ -628,7 +627,7 @@ class MessageInputState extends State<MessageInput> {
color: _messageInputTheme.expandButtonColor, color: _messageInputTheme.expandButtonColor,
), ),
), ),
padding: const EdgeInsets.all(0), padding: EdgeInsets.zero,
constraints: const BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 24, height: 24,
width: 24, width: 24,
@@ -797,7 +796,7 @@ class MessageInputState extends State<MessageInput> {
child: IconButton( child: IconButton(
icon: StreamSvgIcon.closeSmall(), icon: StreamSvgIcon.closeSmall(),
splashRadius: 24, splashRadius: 24,
padding: const EdgeInsets.all(0), padding: EdgeInsets.zero,
constraints: const BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 24, height: 24,
width: 24, width: 24,
@@ -980,7 +979,7 @@ class MessageInputState extends State<MessageInput> {
return AnimatedContainer( return AnimatedContainer(
duration: _openFilePickerSection duration: _openFilePickerSection
? const Duration(milliseconds: 300) ? const Duration(milliseconds: 300)
: const Duration(), : Duration.zero,
curve: Curves.easeOut, curve: Curves.easeOut,
height: _openFilePickerSection ? _kMinMediaPickerSize : 0, height: _openFilePickerSection ? _kMinMediaPickerSize : 0,
child: SingleChildScrollView( child: SingleChildScrollView(
@@ -1034,7 +1033,7 @@ class MessageInputState extends State<MessageInput> {
}, },
), ),
IconButton( IconButton(
padding: const EdgeInsets.all(0), padding: EdgeInsets.zero,
icon: StreamSvgIcon.record( icon: StreamSvgIcon.record(
color: _getIconColor(3), color: _getIconColor(3),
), ),
@@ -1436,9 +1435,9 @@ class MessageInputState extends State<MessageInput> {
], ],
); );
default: default:
return Container( return const ColoredBox(
color: Colors.black26, color: Colors.black26,
child: const Icon(Icons.insert_drive_file), child: Icon(Icons.insert_drive_file),
); );
} }
} }
@@ -1453,7 +1452,7 @@ class MessageInputState extends State<MessageInput> {
? _messageInputTheme.actionButtonColor ? _messageInputTheme.actionButtonColor
: _messageInputTheme.actionButtonIdleColor), : _messageInputTheme.actionButtonIdleColor),
), ),
padding: const EdgeInsets.all(0), padding: EdgeInsets.zero,
constraints: const BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 24, height: 24,
width: 24, width: 24,
@@ -1482,7 +1481,7 @@ class MessageInputState extends State<MessageInput> {
? _messageInputTheme.actionButtonColor ? _messageInputTheme.actionButtonColor
: _messageInputTheme.actionButtonIdleColor, : _messageInputTheme.actionButtonIdleColor,
), ),
padding: const EdgeInsets.all(0), padding: EdgeInsets.zero,
constraints: const BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 24, height: 24,
width: 24, width: 24,
@@ -1699,7 +1698,7 @@ class MessageInputState extends State<MessageInput> {
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: IconButton( child: IconButton(
onPressed: sendMessage, onPressed: sendMessage,
padding: const EdgeInsets.all(0), padding: EdgeInsets.zero,
splashRadius: 24, splashRadius: 24,
constraints: const BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 24, height: 24,
@@ -1926,7 +1925,6 @@ class MessageInputState extends State<MessageInput> {
class _PickerWidget extends StatefulWidget { class _PickerWidget extends StatefulWidget {
const _PickerWidget({ const _PickerWidget({
Key? key,
required this.filePickerIndex, required this.filePickerIndex,
required this.containsFile, required this.containsFile,
required this.selectedMedias, required this.selectedMedias,
@@ -1934,7 +1932,7 @@ class _PickerWidget extends StatefulWidget {
required this.onMediaSelected, required this.onMediaSelected,
required this.streamChatTheme, required this.streamChatTheme,
required this.mediaListViewController, required this.mediaListViewController,
}) : super(key: key); });
final int filePickerIndex; final int filePickerIndex;
final bool containsFile; final bool containsFile;
@@ -2002,7 +2000,7 @@ class _PickerWidgetState extends State<_PickerWidget> {
onTap: () async { onTap: () async {
PhotoManager.openSetting(); PhotoManager.openSetting();
}, },
child: Container( child: ColoredBox(
color: widget.streamChatTheme.colorTheme.inputBg, color: widget.streamChatTheme.colorTheme.inputBg,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@@ -2040,10 +2038,7 @@ class _PickerWidgetState extends State<_PickerWidget> {
} }
class _CountdownButton extends StatelessWidget { class _CountdownButton extends StatelessWidget {
const _CountdownButton({ const _CountdownButton({required this.count});
Key? key,
required this.count,
}) : super(key: key);
final int count; final int count;
@@ -169,7 +169,7 @@ typedef MessageListView = StreamMessageListView;
class StreamMessageListView extends StatefulWidget { class StreamMessageListView extends StatefulWidget {
/// Instantiate a new StreamMessageListView. /// Instantiate a new StreamMessageListView.
const StreamMessageListView({ const StreamMessageListView({
Key? key, super.key,
this.showScrollToBottom = true, this.showScrollToBottom = true,
this.scrollToBottomBuilder, this.scrollToBottomBuilder,
this.messageBuilder, this.messageBuilder,
@@ -206,7 +206,7 @@ class StreamMessageListView extends StatefulWidget {
this.paginationLoadingIndicatorBuilder, this.paginationLoadingIndicatorBuilder,
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.onDrag, this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.onDrag,
this.spacingWidgetBuilder, this.spacingWidgetBuilder,
}) : super(key: key); });
/// [ScrollViewKeyboardDismissBehavior] the defines how this [PositionedList] will /// [ScrollViewKeyboardDismissBehavior] the defines how this [PositionedList] will
/// dismiss the keyboard automatically. /// dismiss the keyboard automatically.
@@ -751,7 +751,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
StreamMessageListViewTheme.of(context).backgroundImage; StreamMessageListViewTheme.of(context).backgroundImage;
if (backgroundColor != null || backgroundImage != null) { if (backgroundColor != null || backgroundImage != null) {
return Container( return DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
color: backgroundColor, color: backgroundColor,
image: backgroundImage, image: backgroundImage,
@@ -889,7 +889,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
initialIndex = 0; initialIndex = 0;
await streamChannel!.reloadChannel(); await streamChannel!.reloadChannel();
WidgetsBinding.instance?.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollController!.jumpTo(index: 0); _scrollController!.jumpTo(index: 0);
}); });
} else { } else {
@@ -1039,9 +1039,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
} }
}, },
onMessageTap: (message) { onMessageTap: (message) {
if (widget.onMessageTap != null) { widget.onMessageTap?.call(message);
widget.onMessageTap!(message);
}
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
}, },
showPinButton: currentUserMember != null && showPinButton: currentUserMember != null &&
@@ -1066,9 +1064,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
StreamSystemMessage( StreamSystemMessage(
message: message, message: message,
onMessageTap: (message) { onMessageTap: (message) {
if (widget.onSystemMessageTap != null) { widget.onSystemMessageTap?.call(message);
widget.onSystemMessageTap!(message);
}
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
}, },
); );
@@ -1230,9 +1226,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
} }
}, },
onMessageTap: (message) { onMessageTap: (message) {
if (widget.onMessageTap != null) { widget.onMessageTap?.call(message);
widget.onMessageTap!(message);
}
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
}, },
showPinButton: currentUserMember != null && showPinButton: currentUserMember != null &&
@@ -1287,8 +1281,8 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
), ),
duration: const Duration(seconds: 3), duration: const Duration(seconds: 3),
onEnd: () => initialMessageHighlightComplete = true, onEnd: () => initialMessageHighlightComplete = true,
builder: (_, color, child) => Container( builder: (_, color, child) => ColoredBox(
color: color, color: color!,
child: child, child: child,
), ),
child: Padding( child: Padding(
@@ -1341,7 +1335,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
if (event.message?.parentId == widget.parentMessage?.id && if (event.message?.parentId == widget.parentMessage?.id &&
event.message!.user!.id == event.message!.user!.id ==
streamChannel!.channel.client.state.currentUser!.id) { streamChannel!.channel.client.state.currentUser!.id) {
WidgetsBinding.instance!.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollController?.scrollTo( _scrollController?.scrollTo(
index: 0, index: 0,
duration: const Duration(seconds: 1), duration: const Duration(seconds: 1),
@@ -1389,11 +1383,10 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
void _getOnThreadTap() { void _getOnThreadTap() {
if (widget.onThreadTap != null) { if (widget.onThreadTap != null) {
_onThreadTap = (Message message) { _onThreadTap = (Message message) {
final threadBuilder = widget.threadBuilder;
widget.onThreadTap!( widget.onThreadTap!(
message, message,
widget.threadBuilder != null threadBuilder != null ? threadBuilder(context, message) : null,
? widget.threadBuilder!(context, message)
: null,
); );
}; };
} else if (widget.threadBuilder != null) { } else if (widget.threadBuilder != null) {
@@ -1431,13 +1424,12 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
class _LoadingIndicator extends StatelessWidget { class _LoadingIndicator extends StatelessWidget {
const _LoadingIndicator({ const _LoadingIndicator({
Key? key,
required this.streamTheme, required this.streamTheme,
required this.isThreadConversation, required this.isThreadConversation,
required this.direction, required this.direction,
required this.streamChannel, required this.streamChannel,
this.indicatorBuilder, this.indicatorBuilder,
}) : super(key: key); });
final StreamChatThemeData streamTheme; final StreamChatThemeData streamTheme;
final bool isThreadConversation; final bool isThreadConversation;
@@ -1454,7 +1446,7 @@ class _LoadingIndicator extends StatelessWidget {
key: Key('LOADING-INDICATOR $direction'), key: Key('LOADING-INDICATOR $direction'),
stream: stream, stream: stream,
initialData: false, initialData: false,
errorBuilder: (context, error) => Container( errorBuilder: (context, error) => ColoredBox(
color: streamTheme.colorTheme.accentError.withOpacity(0.2), color: streamTheme.colorTheme.accentError.withOpacity(0.2),
child: Center( child: Center(
child: Text(context.translations.loadingMessagesError), child: Text(context.translations.loadingMessagesError),
@@ -15,14 +15,14 @@ typedef MessageReactionsModal = StreamMessageReactionsModal;
class StreamMessageReactionsModal extends StatelessWidget { class StreamMessageReactionsModal extends StatelessWidget {
/// Constructor for creating a [StreamMessageReactionsModal] reactions /// Constructor for creating a [StreamMessageReactionsModal] reactions
const StreamMessageReactionsModal({ const StreamMessageReactionsModal({
Key? key, super.key,
required this.message, required this.message,
required this.messageWidget, required this.messageWidget,
required this.messageTheme, required this.messageTheme,
this.showReactions, this.showReactions,
this.reverse = false, this.reverse = false,
this.onUserAvatarTap, this.onUserAvatarTap,
}) : super(key: key); });
/// Widget that shows the message /// Widget that shows the message
final Widget messageWidget; final Widget messageWidget;
@@ -16,11 +16,11 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
class MessageSearchItem extends StatelessWidget { class MessageSearchItem extends StatelessWidget {
/// Instantiate a new MessageSearchItem /// Instantiate a new MessageSearchItem
const MessageSearchItem({ const MessageSearchItem({
Key? key, super.key,
required this.getMessageResponse, required this.getMessageResponse,
this.onTap, this.onTap,
this.showOnlineStatus = true, this.showOnlineStatus = true,
}) : super(key: key); });
/// [Message] displayed /// [Message] displayed
final GetMessageResponse getMessageResponse; final GetMessageResponse getMessageResponse;
@@ -54,8 +54,9 @@ typedef EmptyMessageSearchBuilder = Widget Function(
@Deprecated("Use 'StreamMessageSearchListView' instead") @Deprecated("Use 'StreamMessageSearchListView' instead")
class MessageSearchListView extends StatefulWidget { class MessageSearchListView extends StatefulWidget {
/// Instantiate a new MessageSearchListView /// Instantiate a new MessageSearchListView
@Deprecated("Use 'StreamMessageSearchListView' instead")
const MessageSearchListView({ const MessageSearchListView({
Key? key, super.key,
required this.filters, required this.filters,
this.messageQuery, this.messageQuery,
this.sortOptions, this.sortOptions,
@@ -72,7 +73,7 @@ class MessageSearchListView extends StatefulWidget {
this.loadingBuilder, this.loadingBuilder,
this.childBuilder, this.childBuilder,
this.messageSearchListController, this.messageSearchListController,
}) : super(key: key); });
/// Message String to search on /// Message String to search on
final String? messageQuery; final String? messageQuery;
@@ -238,7 +239,7 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
initialData: false, initialData: false,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasError) { if (snapshot.hasError) {
return Container( return ColoredBox(
color: StreamChatTheme.of(context) color: StreamChatTheme.of(context)
.colorTheme .colorTheme
.accentError .accentError
@@ -14,12 +14,12 @@ typedef MessageText = StreamMessageText;
class StreamMessageText extends StatelessWidget { class StreamMessageText extends StatelessWidget {
/// Constructor for creating a [StreamMessageText] widget /// Constructor for creating a [StreamMessageText] widget
const StreamMessageText({ const StreamMessageText({
Key? key, super.key,
required this.message, required this.message,
required this.messageTheme, required this.messageTheme,
this.onMentionTap, this.onMentionTap,
this.onLinkTap, this.onLinkTap,
}) : super(key: key); });
/// Message whose text is to be displayed /// Message whose text is to be displayed
final Message message; final Message message;
@@ -52,7 +52,7 @@ typedef MessageWidget = StreamMessageWidget;
class StreamMessageWidget extends StatefulWidget { class StreamMessageWidget extends StatefulWidget {
/// Creates a new instance of the message widget. /// Creates a new instance of the message widget.
StreamMessageWidget({ StreamMessageWidget({
Key? key, super.key,
required this.message, required this.message,
required this.messageTheme, required this.messageTheme,
this.reverse = false, this.reverse = false,
@@ -105,7 +105,7 @@ class StreamMessageWidget extends StatefulWidget {
this.customActions = const [], this.customActions = const [],
this.onAttachmentTap, this.onAttachmentTap,
this.usernameBuilder, this.usernameBuilder,
}) : attachmentBuilders = { }) : attachmentBuilders = {
'image': (context, message, attachments) { 'image': (context, message, attachments) {
final border = RoundedRectangleBorder( final border = RoundedRectangleBorder(
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero, borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
@@ -261,8 +261,7 @@ class StreamMessageWidget extends StatefulWidget {
.toList(), .toList(),
); );
}, },
}..addAll(customAttachmentBuilders ?? {}), }..addAll(customAttachmentBuilders ?? {});
super(key: key);
/// Function called on mention tap /// Function called on mention tap
final void Function(User)? onMentionTap; final void Function(User)? onMentionTap;
@@ -878,7 +877,7 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
const Offstage(); const Offstage();
} }
final children = <Widget>[]; final children = <WidgetSpan>[];
final threadParticipants = widget.message.threadParticipants?.take(2); final threadParticipants = widget.message.threadParticipants?.take(2);
final showThreadParticipants = threadParticipants?.isNotEmpty == true; final showThreadParticipants = threadParticipants?.isNotEmpty == true;
@@ -909,68 +908,72 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
const usernameKey = Key('username'); const usernameKey = Key('username');
children.addAll([ children.addAll([
if (showUsername) _buildUsername(usernameKey), if (showUsername) WidgetSpan(child: _buildUsername(usernameKey)),
if (showTimeStamp) if (showTimeStamp)
Text( WidgetSpan(
Jiffy(widget.message.createdAt.toLocal()).jm, child: Text(
style: widget.messageTheme.createdAtStyle, Jiffy(widget.message.createdAt.toLocal()).jm,
style: widget.messageTheme.createdAtStyle,
),
),
if (showSendingIndicator)
WidgetSpan(
child: _buildSendingIndicator(),
), ),
if (showSendingIndicator) _buildSendingIndicator(),
]); ]);
final showThreadTail = !(hasUrlAttachments || isGiphy || isOnlyEmoji) && final showThreadTail = !(hasUrlAttachments || isGiphy || isOnlyEmoji) &&
(showThreadReplyIndicator || showInChannel); (showThreadReplyIndicator || showInChannel);
final threadIndicatorWidgets = <Widget>[ final threadIndicatorWidgets = <WidgetSpan>[
if (showThreadTail) if (showThreadTail)
Container( WidgetSpan(
margin: EdgeInsets.only( child: Container(
bottom: context.textScaleFactor * margin: EdgeInsets.only(
((widget.messageTheme.repliesStyle?.fontSize ?? 1) / 2), bottom: context.textScaleFactor *
), ((widget.messageTheme.repliesStyle?.fontSize ?? 1) / 2),
child: CustomPaint( ),
size: const Size(16, 32) * context.textScaleFactor, child: CustomPaint(
painter: _ThreadReplyPainter( size: const Size(16, 32) * context.textScaleFactor,
context: context, painter: _ThreadReplyPainter(
color: widget.messageTheme.messageBorderColor, context: context,
reverse: widget.reverse, color: widget.messageTheme.messageBorderColor,
reverse: widget.reverse,
),
), ),
), ),
), ),
if (showInChannel || showThreadReplyIndicator) ...[ if (showInChannel || showThreadReplyIndicator) ...[
if (showThreadParticipants) if (showThreadParticipants)
SizedBox.fromSize( WidgetSpan(
size: Size((threadParticipants!.length * 8.0) + 8, 16), child: SizedBox.fromSize(
child: _buildThreadParticipantsIndicator(threadParticipants), size: Size((threadParticipants!.length * 8.0) + 8, 16),
child: _buildThreadParticipantsIndicator(threadParticipants),
),
),
WidgetSpan(
child: InkWell(
onTap: widget.onThreadTap != null ? onThreadTap : null,
child: Text(msg, style: widget.messageTheme.repliesStyle),
), ),
InkWell(
onTap: widget.onThreadTap != null ? onThreadTap : null,
child: Text(msg, style: widget.messageTheme.repliesStyle),
), ),
], ],
]; ];
return Row( if (widget.reverse) {
crossAxisAlignment: CrossAxisAlignment.end, children.addAll(threadIndicatorWidgets.reversed);
mainAxisAlignment: } else {
widget.reverse ? MainAxisAlignment.end : MainAxisAlignment.start, children.insertAll(0, threadIndicatorWidgets);
children: [ }
if (showThreadTail && !widget.reverse) ...threadIndicatorWidgets,
...children.map( return Text.rich(
(child) { TextSpan(
Widget mappedChild = SizedBox( children: [
height: context.textScaleFactor * 14, ...children,
child: child, ].insertBetween(const WidgetSpan(child: SizedBox(width: 8))),
); ),
if (child.key == usernameKey) { maxLines: 1,
mappedChild = Flexible(child: mappedChild); textAlign: widget.reverse ? TextAlign.right : TextAlign.left,
}
return mappedChild;
},
),
if (showThreadTail && widget.reverse)
...threadIndicatorWidgets.reversed,
].insertBetween(const SizedBox(width: 8)),
); );
} }
@@ -1076,7 +1079,7 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
showTimestamp: false, showTimestamp: false,
translateUserAvatar: false, translateUserAvatar: false,
showSendingIndicator: false, showSendingIndicator: false,
padding: const EdgeInsets.all(0), padding: EdgeInsets.zero,
showReactionPickerIndicator: widget.showReactions && showReactionPickerIndicator: widget.showReactions &&
(widget.message.status == MessageSendingStatus.sent) && (widget.message.status == MessageSendingStatus.sent) &&
channel.ownCapabilities.contains(PermissionType.sendReaction), channel.ownCapabilities.contains(PermissionType.sendReaction),
@@ -1107,6 +1110,13 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
widget.onThreadTap != null, widget.onThreadTap != null,
showFlagButton: widget.showFlagButton, showFlagButton: widget.showFlagButton,
customActions: widget.customActions, customActions: widget.customActions,
showDeleteMessage: widget.showDeleteMessage || isDeleteFailed,
showEditMessage: widget.showEditMessage &&
!isDeleteFailed &&
!widget.message.attachments
.any((element) => element.type == 'giphy'),
showPinButton: widget.showPinButton,
showReactions: widget.showReactions,
), ),
), ),
); );
@@ -1133,7 +1143,7 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
showTimestamp: false, showTimestamp: false,
translateUserAvatar: false, translateUserAvatar: false,
showSendingIndicator: false, showSendingIndicator: false,
padding: const EdgeInsets.all(0), padding: EdgeInsets.zero,
showReactionPickerIndicator: widget.showReactions && showReactionPickerIndicator: widget.showReactions &&
(widget.message.status == MessageSendingStatus.sent) && (widget.message.status == MessageSendingStatus.sent) &&
channel.ownCapabilities.contains(PermissionType.sendReaction), channel.ownCapabilities.contains(PermissionType.sendReaction),
@@ -1259,6 +1269,7 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
); );
if (isMessageRead) { if (isMessageRead) {
child = Row( child = Row(
mainAxisSize: MainAxisSize.min,
children: [ children: [
if (memberCount > 2) if (memberCount > 2)
Text( Text(
@@ -1396,11 +1407,9 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
class _ThreadParticipants extends StatelessWidget { class _ThreadParticipants extends StatelessWidget {
const _ThreadParticipants({ const _ThreadParticipants({
Key? key,
required StreamChatThemeData streamChatTheme, required StreamChatThemeData streamChatTheme,
required this.threadParticipants, required this.threadParticipants,
}) : _streamChatTheme = streamChatTheme, }) : _streamChatTheme = streamChatTheme;
super(key: key);
final StreamChatThemeData _streamChatTheme; final StreamChatThemeData _streamChatTheme;
final Iterable<User> threadParticipants; final Iterable<User> threadParticipants;
@@ -1423,7 +1432,7 @@ class _ThreadParticipants extends StatelessWidget {
padding: const EdgeInsets.all(1), padding: const EdgeInsets.all(1),
child: StreamUserAvatar( child: StreamUserAvatar(
user: user, user: user,
constraints: BoxConstraints.loose(const Size.fromRadius(7)), constraints: BoxConstraints.tight(const Size.fromRadius(7)),
showOnlineStatus: false, showOnlineStatus: false,
), ),
), ),
@@ -17,12 +17,12 @@ class StreamMultiOverlay extends StatelessWidget {
/// [childAnchor] - the anchor relative to the child /// [childAnchor] - the anchor relative to the child
/// [child] - the child widget /// [child] - the child widget
const StreamMultiOverlay({ const StreamMultiOverlay({
Key? key, super.key,
required this.overlayOptions, required this.overlayOptions,
required this.child, required this.child,
required this.overlayAnchor, required this.overlayAnchor,
required this.childAnchor, required this.childAnchor,
}) : super(key: key); });
/// The list of overlay options /// The list of overlay options
final List<OverlayOptions> overlayOptions; final List<OverlayOptions> overlayOptions;
@@ -11,7 +11,7 @@ typedef OptionListTile = StreamOptionListTile;
class StreamOptionListTile extends StatelessWidget { class StreamOptionListTile extends StatelessWidget {
/// Constructor for creating [StreamOptionListTile] /// Constructor for creating [StreamOptionListTile]
const StreamOptionListTile({ const StreamOptionListTile({
Key? key, super.key,
required this.title, required this.title,
this.leading, this.leading,
this.trailing, this.trailing,
@@ -20,7 +20,7 @@ class StreamOptionListTile extends StatelessWidget {
this.tileColor, this.tileColor,
this.separatorColor, this.separatorColor,
this.titleTextStyle, this.titleTextStyle,
}) : super(key: key); });
/// Title for tile /// Title for tile
final String title; final String title;
@@ -18,7 +18,7 @@ typedef QuotedMessageWidget = StreamQuotedMessageWidget;
class StreamQuotedMessageWidget extends StatelessWidget { class StreamQuotedMessageWidget extends StatelessWidget {
/// Creates a new instance of the widget. /// Creates a new instance of the widget.
const StreamQuotedMessageWidget({ const StreamQuotedMessageWidget({
Key? key, super.key,
required this.message, required this.message,
required this.messageTheme, required this.messageTheme,
this.reverse = false, this.reverse = false,
@@ -27,7 +27,7 @@ class StreamQuotedMessageWidget extends StatelessWidget {
this.attachmentThumbnailBuilders, this.attachmentThumbnailBuilders,
this.padding = const EdgeInsets.all(8), this.padding = const EdgeInsets.all(8),
this.onTap, this.onTap,
}) : super(key: key); });
/// The message /// The message
final Message message; final Message message;
@@ -251,12 +251,10 @@ class StreamQuotedMessageWidget extends StatelessWidget {
class _VideoAttachmentThumbnail extends StatefulWidget { class _VideoAttachmentThumbnail extends StatefulWidget {
const _VideoAttachmentThumbnail({ const _VideoAttachmentThumbnail({
Key? key, super.key,
required this.attachment, required this.attachment,
this.size = const Size(32, 32), });
}) : super(key: key);
final Size size;
final Attachment attachment; final Attachment attachment;
@override @override
@@ -285,8 +283,8 @@ class _VideoAttachmentThumbnailState extends State<_VideoAttachmentThumbnail> {
@override @override
Widget build(BuildContext context) => SizedBox( Widget build(BuildContext context) => SizedBox(
height: widget.size.height, height: 32,
width: widget.size.width, width: 32,
child: _controller.value.isInitialized child: _controller.value.isInitialized
? VideoPlayer(_controller) ? VideoPlayer(_controller)
: const CircularProgressIndicator(), : const CircularProgressIndicator(),
@@ -14,7 +14,7 @@ typedef ReactionBubble = StreamReactionBubble;
class StreamReactionBubble extends StatelessWidget { class StreamReactionBubble extends StatelessWidget {
/// Constructor for creating a [StreamReactionBubble] /// Constructor for creating a [StreamReactionBubble]
const StreamReactionBubble({ const StreamReactionBubble({
Key? key, super.key,
required this.reactions, required this.reactions,
required this.borderColor, required this.borderColor,
required this.backgroundColor, required this.backgroundColor,
@@ -23,7 +23,7 @@ class StreamReactionBubble extends StatelessWidget {
this.flipTail = false, this.flipTail = false,
this.highlightOwnReactions = true, this.highlightOwnReactions = true,
this.tailCirclesSpacing = 0, this.tailCirclesSpacing = 0,
}) : super(key: key); });
/// Reactions to show /// Reactions to show
final List<Reaction> reactions; final List<Reaction> reactions;
@@ -19,9 +19,9 @@ typedef ReactionPicker = StreamReactionPicker;
class StreamReactionPicker extends StatefulWidget { class StreamReactionPicker extends StatefulWidget {
/// Constructor for creating a [StreamReactionPicker] widget /// Constructor for creating a [StreamReactionPicker] widget
const StreamReactionPicker({ const StreamReactionPicker({
Key? key, super.key,
required this.message, required this.message,
}) : super(key: key); });
/// Message to attach the reaction to /// Message to attach the reaction to
final Message message; final Message message;
@@ -11,11 +11,11 @@ typedef SendingIndicator = StreamSendingIndicator;
class StreamSendingIndicator extends StatelessWidget { class StreamSendingIndicator extends StatelessWidget {
/// Constructor for creating a [StreamSendingIndicator] widget /// Constructor for creating a [StreamSendingIndicator] widget
const StreamSendingIndicator({ const StreamSendingIndicator({
Key? key, super.key,
required this.message, required this.message,
this.isMessageRead = false, this.isMessageRead = false,
this.size = 12, this.size = 12,
}) : super(key: key); });
/// Message for sending indicator /// Message for sending indicator
final Message message; final Message message;

Some files were not shown because too many files have changed in this diff Show More