Merge branch 'develop'
This commit is contained in:
@@ -39,36 +39,36 @@ jobs:
|
||||
run: melos bootstrap
|
||||
|
||||
- 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:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
relative_path: 'packages/stream_chat'
|
||||
folders: ${{ env.folders }}
|
||||
|
||||
- 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:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
relative_path: 'packages/stream_chat_flutter_core'
|
||||
folders: ${{ env.folders }}
|
||||
|
||||
- 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:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
relative_path: 'packages/stream_chat_flutter'
|
||||
folders: ${{ env.folders }}
|
||||
|
||||
- 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:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
relative_path: 'packages/stream_chat_localizations'
|
||||
folders: ${{ env.folders }}
|
||||
|
||||
- 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:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
relative_path: 'packages/stream_chat_persistence'
|
||||
folders: ${{ env.folders }}
|
||||
folders: ${{ env.folders }}
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# Fast fail the script on failures.
|
||||
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"
|
||||
run: |
|
||||
flutter pub global activate melos ${{ env.melos_version }}
|
||||
pub global activate remove_from_coverage
|
||||
flutter pub global activate remove_from_coverage
|
||||
- name: "Bootstrap Workspace"
|
||||
run: melos bootstrap
|
||||
- name: "Flutter Test"
|
||||
|
||||
@@ -147,6 +147,21 @@ linter:
|
||||
- tighten_type_of_initializing_formals
|
||||
- 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
|
||||
dart_code_metrics:
|
||||
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/).
|
||||
+13
-2
@@ -1,4 +1,5 @@
|
||||
name: stream_chat_flutter
|
||||
repository: https://github.com/GetStream/stream-chat-flutter
|
||||
|
||||
versioning:
|
||||
mode: independent
|
||||
@@ -7,6 +8,10 @@ packages:
|
||||
- packages/**
|
||||
|
||||
scripts:
|
||||
postclean:
|
||||
run: melos run clean:flutter --no-select
|
||||
description: Runs "flutter clean" in all Flutter packages
|
||||
|
||||
lint:all:
|
||||
run: melos run analyze && melos run format
|
||||
description: Run all static analysis checks
|
||||
@@ -74,6 +79,12 @@ scripts:
|
||||
flutter: true
|
||||
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:
|
||||
run: |
|
||||
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
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
flutter: '>=1.17.0 <2.0.0'
|
||||
sdk: '>=2.17.0 <3.0.0'
|
||||
flutter: '>=1.17.0 <3.0.0'
|
||||
@@ -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
|
||||
|
||||
✅ Added
|
||||
|
||||
@@ -43,10 +43,10 @@ class StreamExample extends StatelessWidget {
|
||||
/// To initialize this example, an instance of
|
||||
/// [client] and [channel] is required.
|
||||
const StreamExample({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.client,
|
||||
required this.channel,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Instance of [StreamChatClient] we created earlier.
|
||||
/// This contains information about our application and connection state.
|
||||
@@ -67,9 +67,9 @@ class StreamExample extends StatelessWidget {
|
||||
class HomeScreen extends StatelessWidget {
|
||||
/// [HomeScreen] is constructed using the [Channel] we defined earlier.
|
||||
const HomeScreen({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.channel,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Channel object containing the [Channel.id] we'd like to observe.
|
||||
final Channel channel;
|
||||
@@ -119,10 +119,10 @@ class HomeScreen extends StatelessWidget {
|
||||
class MessageView extends StatefulWidget {
|
||||
/// Message takes the latest list of messages and the current channel.
|
||||
const MessageView({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.messages,
|
||||
required this.channel,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// List of messages sent in the given channel.
|
||||
final List<Message> messages;
|
||||
|
||||
@@ -5,7 +5,7 @@ publish_to: "none"
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
sdk: '>=2.17.0 <3.0.0'
|
||||
|
||||
dependencies:
|
||||
cupertino_icons: ^1.0.0
|
||||
|
||||
@@ -171,6 +171,18 @@ class Channel {
|
||||
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.
|
||||
User? get createdBy {
|
||||
_checkInitialized();
|
||||
@@ -195,6 +207,42 @@ class Channel {
|
||||
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
|
||||
int get cooldown {
|
||||
_checkInitialized();
|
||||
@@ -1540,6 +1588,8 @@ class ChannelClientState {
|
||||
|
||||
_listenMemberRemoved();
|
||||
|
||||
_listenMemberUpdated();
|
||||
|
||||
_listenMemberBanned();
|
||||
|
||||
_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() {
|
||||
_subscriptions.add(_channel.on(EventType.channelUpdated).listen((Event e) {
|
||||
final channel = e.channel!;
|
||||
@@ -2173,7 +2235,7 @@ class ChannelClientState {
|
||||
|
||||
/// The channel threads related to this channel.
|
||||
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.
|
||||
Stream<Map<String, List<Message>>> get threadsStream =>
|
||||
|
||||
@@ -10,15 +10,6 @@ enum PushProvider {
|
||||
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
|
||||
class DeviceApi {
|
||||
/// 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:json_annotation/json_annotation.dart';
|
||||
|
||||
@@ -69,6 +71,11 @@ class PaginationParams extends Equatable {
|
||||
this.greaterThanOrEqual,
|
||||
this.lessThan,
|
||||
this.lessThanOrEqual,
|
||||
this.createdAtAfterOrEqual,
|
||||
this.createdAtAfter,
|
||||
this.createdAtBeforeOrEqual,
|
||||
this.createdAtBefore,
|
||||
this.createdAtAround,
|
||||
}) : assert(
|
||||
offset == null || offset == 0 || next == null,
|
||||
'Cannot specify non-zero `offset` with `next` parameter',
|
||||
@@ -82,9 +89,11 @@ class PaginationParams extends Equatable {
|
||||
final int limit;
|
||||
|
||||
/// The amount of items requested before message ID from the APIs.
|
||||
@Deprecated('before is deprecated, use limit instead')
|
||||
final int before;
|
||||
|
||||
/// The amount of items requested after message ID from the APIs.
|
||||
@Deprecated('after is deprecated, use limit instead')
|
||||
final int after;
|
||||
|
||||
/// The offset of requesting items.
|
||||
@@ -113,6 +122,26 @@ class PaginationParams extends Equatable {
|
||||
@JsonKey(name: 'id_lte')
|
||||
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
|
||||
Map<String, dynamic> toJson() => _$PaginationParamsToJson(this);
|
||||
|
||||
@@ -128,6 +157,11 @@ class PaginationParams extends Equatable {
|
||||
String? greaterThanOrEqual,
|
||||
String? lessThan,
|
||||
String? lessThanOrEqual,
|
||||
DateTime? createdAtAfterOrEqual,
|
||||
DateTime? createdAtAfter,
|
||||
DateTime? createdAtBeforeOrEqual,
|
||||
DateTime? createdAtBefore,
|
||||
DateTime? createdAtAround,
|
||||
}) =>
|
||||
PaginationParams(
|
||||
limit: limit ?? this.limit,
|
||||
@@ -140,6 +174,13 @@ class PaginationParams extends Equatable {
|
||||
greaterThanOrEqual: greaterThanOrEqual ?? this.greaterThanOrEqual,
|
||||
lessThan: lessThan ?? this.lessThan,
|
||||
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
|
||||
|
||||
@@ -30,6 +30,21 @@ PaginationParams _$PaginationParamsFromJson(Map<String, dynamic> json) =>
|
||||
greaterThanOrEqual: json['id_gte'] as String?,
|
||||
lessThan: json['id_lt'] 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) {
|
||||
@@ -52,6 +67,15 @@ Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) {
|
||||
writeNotNull('id_gte', instance.greaterThanOrEqual);
|
||||
writeNotNull('id_lt', instance.lessThan);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@ class StreamChatError with EquatableMixin implements Exception {
|
||||
class StreamWebSocketError extends StreamChatError {
|
||||
///
|
||||
const StreamWebSocketError(
|
||||
String message, {
|
||||
super.message, {
|
||||
this.data,
|
||||
}) : super(message);
|
||||
});
|
||||
|
||||
///
|
||||
factory StreamWebSocketError.fromStreamError(Map<String, Object?> error) {
|
||||
|
||||
@@ -36,7 +36,7 @@ class AuthInterceptor extends Interceptor {
|
||||
final params = {'user_id': token.userId};
|
||||
final headers = {
|
||||
'Authorization': token.rawValue,
|
||||
'stream-auth-type': token.authType.raw,
|
||||
'stream-auth-type': token.authType.name,
|
||||
};
|
||||
options
|
||||
..queryParameters.addAll(params)
|
||||
|
||||
@@ -6,14 +6,11 @@ class StreamChatDioError extends DioError {
|
||||
/// Initialize a stream chat dio error
|
||||
StreamChatDioError({
|
||||
required this.error,
|
||||
required RequestOptions requestOptions,
|
||||
Response? response,
|
||||
DioErrorType type = DioErrorType.other,
|
||||
required super.requestOptions,
|
||||
super.response,
|
||||
super.type,
|
||||
}) : super(
|
||||
error: error,
|
||||
requestOptions: requestOptions,
|
||||
response: response,
|
||||
type: type,
|
||||
);
|
||||
|
||||
@override
|
||||
|
||||
@@ -18,15 +18,6 @@ enum AuthType {
|
||||
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.
|
||||
class Token extends Equatable {
|
||||
const Token._({
|
||||
|
||||
@@ -90,6 +90,9 @@ class AttachmentFile {
|
||||
/// Union class to hold various [UploadState] of a attachment.
|
||||
@freezed
|
||||
class UploadState with _$UploadState {
|
||||
// Dummy private constructor in order to use getters
|
||||
const UploadState._();
|
||||
|
||||
/// Preparing state of the union
|
||||
const factory UploadState.preparing() = Preparing;
|
||||
|
||||
@@ -108,10 +111,7 @@ class UploadState with _$UploadState {
|
||||
/// Creates a new instance from a json
|
||||
factory UploadState.fromJson(Map<String, dynamic> json) =>
|
||||
_$UploadStateFromJson(json);
|
||||
}
|
||||
|
||||
/// Helper extension for UploadState
|
||||
extension UploadStateX on UploadState? {
|
||||
/// Returns true if state is [Preparing]
|
||||
bool get isPreparing => this is Preparing;
|
||||
|
||||
|
||||
@@ -103,25 +103,29 @@ class _$UploadStateCopyWithImpl<$Res> implements $UploadStateCopyWith<$Res> {
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $PreparingCopyWith<$Res> {
|
||||
factory $PreparingCopyWith(Preparing value, $Res Function(Preparing) then) =
|
||||
_$PreparingCopyWithImpl<$Res>;
|
||||
abstract class _$$PreparingCopyWith<$Res> {
|
||||
factory _$$PreparingCopyWith(
|
||||
_$Preparing value, $Res Function(_$Preparing) then) =
|
||||
__$$PreparingCopyWithImpl<$Res>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$PreparingCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
||||
implements $PreparingCopyWith<$Res> {
|
||||
_$PreparingCopyWithImpl(Preparing _value, $Res Function(Preparing) _then)
|
||||
: super(_value, (v) => _then(v as Preparing));
|
||||
class __$$PreparingCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
||||
implements _$$PreparingCopyWith<$Res> {
|
||||
__$$PreparingCopyWithImpl(
|
||||
_$Preparing _value, $Res Function(_$Preparing) _then)
|
||||
: super(_value, (v) => _then(v as _$Preparing));
|
||||
|
||||
@override
|
||||
Preparing get _value => super._value as Preparing;
|
||||
_$Preparing get _value => super._value as _$Preparing;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$Preparing implements Preparing {
|
||||
const _$Preparing({final String? $type}) : $type = $type ?? 'preparing';
|
||||
class _$Preparing extends Preparing {
|
||||
const _$Preparing({final String? $type})
|
||||
: $type = $type ?? 'preparing',
|
||||
super._();
|
||||
|
||||
factory _$Preparing.fromJson(Map<String, dynamic> json) =>
|
||||
_$$PreparingFromJson(json);
|
||||
@@ -137,7 +141,7 @@ class _$Preparing implements Preparing {
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType && other is Preparing);
|
||||
(other.runtimeType == runtimeType && other is _$Preparing);
|
||||
}
|
||||
|
||||
@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 Preparing._() : super._();
|
||||
|
||||
factory Preparing.fromJson(Map<String, dynamic> json) = _$Preparing.fromJson;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $InProgressCopyWith<$Res> {
|
||||
factory $InProgressCopyWith(
|
||||
InProgress value, $Res Function(InProgress) then) =
|
||||
_$InProgressCopyWithImpl<$Res>;
|
||||
abstract class _$$InProgressCopyWith<$Res> {
|
||||
factory _$$InProgressCopyWith(
|
||||
_$InProgress value, $Res Function(_$InProgress) then) =
|
||||
__$$InProgressCopyWithImpl<$Res>;
|
||||
$Res call({int uploaded, int total});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
||||
implements $InProgressCopyWith<$Res> {
|
||||
_$InProgressCopyWithImpl(InProgress _value, $Res Function(InProgress) _then)
|
||||
: super(_value, (v) => _then(v as InProgress));
|
||||
class __$$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
||||
implements _$$InProgressCopyWith<$Res> {
|
||||
__$$InProgressCopyWithImpl(
|
||||
_$InProgress _value, $Res Function(_$InProgress) _then)
|
||||
: super(_value, (v) => _then(v as _$InProgress));
|
||||
|
||||
@override
|
||||
InProgress get _value => super._value as InProgress;
|
||||
_$InProgress get _value => super._value as _$InProgress;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? uploaded = freezed,
|
||||
Object? total = freezed,
|
||||
}) {
|
||||
return _then(InProgress(
|
||||
return _then(_$InProgress(
|
||||
uploaded: uploaded == freezed
|
||||
? _value.uploaded
|
||||
: uploaded // ignore: cast_nullable_to_non_nullable
|
||||
@@ -267,10 +273,11 @@ class _$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$InProgress implements InProgress {
|
||||
class _$InProgress extends InProgress {
|
||||
const _$InProgress(
|
||||
{required this.uploaded, required this.total, final String? $type})
|
||||
: $type = $type ?? 'inProgress';
|
||||
: $type = $type ?? 'inProgress',
|
||||
super._();
|
||||
|
||||
factory _$InProgress.fromJson(Map<String, dynamic> json) =>
|
||||
_$$InProgressFromJson(json);
|
||||
@@ -292,7 +299,7 @@ class _$InProgress implements InProgress {
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is InProgress &&
|
||||
other is _$InProgress &&
|
||||
const DeepCollectionEquality().equals(other.uploaded, uploaded) &&
|
||||
const DeepCollectionEquality().equals(other.total, total));
|
||||
}
|
||||
@@ -306,8 +313,8 @@ class _$InProgress implements InProgress {
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
$InProgressCopyWith<InProgress> get copyWith =>
|
||||
_$InProgressCopyWithImpl<InProgress>(this, _$identity);
|
||||
_$$InProgressCopyWith<_$InProgress> get copyWith =>
|
||||
__$$InProgressCopyWithImpl<_$InProgress>(this, _$identity);
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
@@ -389,9 +396,10 @@ class _$InProgress implements InProgress {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class InProgress implements UploadState {
|
||||
abstract class InProgress extends UploadState {
|
||||
const factory InProgress(
|
||||
{required final int uploaded, required final int total}) = _$InProgress;
|
||||
const InProgress._() : super._();
|
||||
|
||||
factory InProgress.fromJson(Map<String, dynamic> json) =
|
||||
_$InProgress.fromJson;
|
||||
@@ -399,30 +407,32 @@ abstract class InProgress implements UploadState {
|
||||
int get uploaded => throw _privateConstructorUsedError;
|
||||
int get total => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$InProgressCopyWith<InProgress> get copyWith =>
|
||||
_$$InProgressCopyWith<_$InProgress> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $SuccessCopyWith<$Res> {
|
||||
factory $SuccessCopyWith(Success value, $Res Function(Success) then) =
|
||||
_$SuccessCopyWithImpl<$Res>;
|
||||
abstract class _$$SuccessCopyWith<$Res> {
|
||||
factory _$$SuccessCopyWith(_$Success value, $Res Function(_$Success) then) =
|
||||
__$$SuccessCopyWithImpl<$Res>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$SuccessCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
||||
implements $SuccessCopyWith<$Res> {
|
||||
_$SuccessCopyWithImpl(Success _value, $Res Function(Success) _then)
|
||||
: super(_value, (v) => _then(v as Success));
|
||||
class __$$SuccessCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
||||
implements _$$SuccessCopyWith<$Res> {
|
||||
__$$SuccessCopyWithImpl(_$Success _value, $Res Function(_$Success) _then)
|
||||
: super(_value, (v) => _then(v as _$Success));
|
||||
|
||||
@override
|
||||
Success get _value => super._value as Success;
|
||||
_$Success get _value => super._value as _$Success;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$Success implements Success {
|
||||
const _$Success({final String? $type}) : $type = $type ?? 'success';
|
||||
class _$Success extends Success {
|
||||
const _$Success({final String? $type})
|
||||
: $type = $type ?? 'success',
|
||||
super._();
|
||||
|
||||
factory _$Success.fromJson(Map<String, dynamic> json) =>
|
||||
_$$SuccessFromJson(json);
|
||||
@@ -438,7 +448,7 @@ class _$Success implements Success {
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType && other is Success);
|
||||
(other.runtimeType == runtimeType && other is _$Success);
|
||||
}
|
||||
|
||||
@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 Success._() : super._();
|
||||
|
||||
factory Success.fromJson(Map<String, dynamic> json) = _$Success.fromJson;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $FailedCopyWith<$Res> {
|
||||
factory $FailedCopyWith(Failed value, $Res Function(Failed) then) =
|
||||
_$FailedCopyWithImpl<$Res>;
|
||||
abstract class _$$FailedCopyWith<$Res> {
|
||||
factory _$$FailedCopyWith(_$Failed value, $Res Function(_$Failed) then) =
|
||||
__$$FailedCopyWithImpl<$Res>;
|
||||
$Res call({String error});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
||||
implements $FailedCopyWith<$Res> {
|
||||
_$FailedCopyWithImpl(Failed _value, $Res Function(Failed) _then)
|
||||
: super(_value, (v) => _then(v as Failed));
|
||||
class __$$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
||||
implements _$$FailedCopyWith<$Res> {
|
||||
__$$FailedCopyWithImpl(_$Failed _value, $Res Function(_$Failed) _then)
|
||||
: super(_value, (v) => _then(v as _$Failed));
|
||||
|
||||
@override
|
||||
Failed get _value => super._value as Failed;
|
||||
_$Failed get _value => super._value as _$Failed;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? error = freezed,
|
||||
}) {
|
||||
return _then(Failed(
|
||||
return _then(_$Failed(
|
||||
error: error == freezed
|
||||
? _value.error
|
||||
: error // ignore: cast_nullable_to_non_nullable
|
||||
@@ -562,9 +573,10 @@ class _$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$Failed implements Failed {
|
||||
class _$Failed extends Failed {
|
||||
const _$Failed({required this.error, final String? $type})
|
||||
: $type = $type ?? 'failed';
|
||||
: $type = $type ?? 'failed',
|
||||
super._();
|
||||
|
||||
factory _$Failed.fromJson(Map<String, dynamic> json) =>
|
||||
_$$FailedFromJson(json);
|
||||
@@ -584,7 +596,7 @@ class _$Failed implements Failed {
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is Failed &&
|
||||
other is _$Failed &&
|
||||
const DeepCollectionEquality().equals(other.error, error));
|
||||
}
|
||||
|
||||
@@ -595,8 +607,8 @@ class _$Failed implements Failed {
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
$FailedCopyWith<Failed> get copyWith =>
|
||||
_$FailedCopyWithImpl<Failed>(this, _$identity);
|
||||
_$$FailedCopyWith<_$Failed> get copyWith =>
|
||||
__$$FailedCopyWithImpl<_$Failed>(this, _$identity);
|
||||
|
||||
@override
|
||||
@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 Failed._() : super._();
|
||||
|
||||
factory Failed.fromJson(Map<String, dynamic> json) = _$Failed.fromJson;
|
||||
|
||||
String get error => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$FailedCopyWith<Failed> get copyWith => throw _privateConstructorUsedError;
|
||||
_$$FailedCopyWith<_$Failed> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -22,9 +22,12 @@ class ChannelModel {
|
||||
DateTime? updatedAt,
|
||||
this.deletedAt,
|
||||
this.memberCount = 0,
|
||||
this.extraData = const {},
|
||||
Map<String, Object?> extraData = const {},
|
||||
this.team,
|
||||
this.cooldown = 0,
|
||||
bool? disabled,
|
||||
bool? hidden,
|
||||
DateTime? truncatedAt,
|
||||
}) : assert(
|
||||
(cid != null && cid.contains(':')) || (id != null && type != null),
|
||||
'provide either a cid or an id and type',
|
||||
@@ -34,7 +37,18 @@ class ChannelModel {
|
||||
cid = cid ?? '$type:$id',
|
||||
config = config ?? ChannelConfig(),
|
||||
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
|
||||
factory ChannelModel.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -92,6 +106,22 @@ class ChannelModel {
|
||||
@JsonKey(includeIfNull: false)
|
||||
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
|
||||
@JsonKey(includeIfNull: false)
|
||||
final Map<String, Object?> extraData;
|
||||
@@ -145,6 +175,9 @@ class ChannelModel {
|
||||
Map<String, Object?>? extraData,
|
||||
String? team,
|
||||
int? cooldown,
|
||||
bool? disabled,
|
||||
bool? hidden,
|
||||
DateTime? truncatedAt,
|
||||
}) =>
|
||||
ChannelModel(
|
||||
id: id ?? this.id,
|
||||
@@ -162,6 +195,14 @@ class ChannelModel {
|
||||
extraData: extraData ?? this.extraData,
|
||||
team: team ?? this.team,
|
||||
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
|
||||
@@ -181,9 +222,12 @@ class ChannelModel {
|
||||
updatedAt: other.updatedAt,
|
||||
deletedAt: other.deletedAt,
|
||||
memberCount: other.memberCount,
|
||||
extraData: {...extraData, ...other.extraData},
|
||||
extraData: other.extraData,
|
||||
team: other.team,
|
||||
cooldown: other.cooldown,
|
||||
disabled: other.disabled,
|
||||
hidden: other.hidden,
|
||||
truncatedAt: other.truncatedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ class ChannelState {
|
||||
this.watcherCount,
|
||||
this.watchers,
|
||||
this.read,
|
||||
this.membership,
|
||||
});
|
||||
|
||||
/// The channel to which this state belongs
|
||||
@@ -42,6 +43,9 @@ class ChannelState {
|
||||
/// The list of channel reads
|
||||
final List<Read>? read;
|
||||
|
||||
/// Relationship of the current user to this channel.
|
||||
final Member? membership;
|
||||
|
||||
/// Create a new instance from a json
|
||||
static ChannelState fromJson(Map<String, dynamic> json) =>
|
||||
_$ChannelStateFromJson(json);
|
||||
@@ -58,6 +62,7 @@ class ChannelState {
|
||||
int? watcherCount,
|
||||
List<User>? watchers,
|
||||
List<Read>? read,
|
||||
Member? membership,
|
||||
}) =>
|
||||
ChannelState(
|
||||
channel: channel ?? this.channel,
|
||||
@@ -67,5 +72,6 @@ class ChannelState {
|
||||
watcherCount: watcherCount ?? this.watcherCount,
|
||||
watchers: watchers ?? this.watchers,
|
||||
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>?)
|
||||
?.map((e) => Read.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
membership: json['membership'] == null
|
||||
? null
|
||||
: Member.fromJson(json['membership'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ChannelStateToJson(ChannelState instance) =>
|
||||
@@ -38,4 +41,5 @@ Map<String, dynamic> _$ChannelStateToJson(ChannelState instance) =>
|
||||
'watcher_count': instance.watcherCount,
|
||||
'watchers': instance.watchers?.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
|
||||
EventChannel({
|
||||
this.members,
|
||||
String? id,
|
||||
String? type,
|
||||
required String cid,
|
||||
required ChannelConfig config,
|
||||
User? createdBy,
|
||||
bool frozen = false,
|
||||
DateTime? lastMessageAt,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
DateTime? deletedAt,
|
||||
int memberCount = 0,
|
||||
super.id,
|
||||
super.type,
|
||||
required String super.cid,
|
||||
super.ownCapabilities,
|
||||
required ChannelConfig super.config,
|
||||
super.createdBy,
|
||||
super.frozen,
|
||||
super.lastMessageAt,
|
||||
required DateTime super.createdAt,
|
||||
required DateTime super.updatedAt,
|
||||
super.deletedAt,
|
||||
super.memberCount,
|
||||
Map<String, Object?>? extraData,
|
||||
int cooldown = 0,
|
||||
String? team,
|
||||
}) : super(
|
||||
id: id,
|
||||
type: type,
|
||||
cid: cid,
|
||||
config: config,
|
||||
createdBy: createdBy,
|
||||
frozen: frozen,
|
||||
lastMessageAt: lastMessageAt,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
deletedAt: deletedAt,
|
||||
memberCount: memberCount,
|
||||
extraData: extraData ?? {},
|
||||
cooldown: cooldown,
|
||||
team: team,
|
||||
);
|
||||
super.cooldown,
|
||||
super.team,
|
||||
super.disabled,
|
||||
super.hidden,
|
||||
super.truncatedAt,
|
||||
}) : super(extraData: extraData ?? {});
|
||||
|
||||
/// Create a new instance from a json
|
||||
factory EventChannel.fromJson(Map<String, dynamic> json) =>
|
||||
|
||||
@@ -81,6 +81,9 @@ EventChannel _$EventChannelFromJson(Map<String, dynamic> json) => EventChannel(
|
||||
id: json['id'] as String?,
|
||||
type: json['type'] 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>),
|
||||
createdBy: json['created_by'] == null
|
||||
? null
|
||||
|
||||
@@ -53,29 +53,43 @@ enum FilterOperator {
|
||||
nor,
|
||||
|
||||
/// Matches any list that contains the specified value
|
||||
contains,
|
||||
}
|
||||
contains;
|
||||
|
||||
/// Helper extension for [FilterOperator]
|
||||
extension FilterOperatorX on FilterOperator {
|
||||
/// Converts [FilterOperator] into rew values
|
||||
String get rawValue => {
|
||||
FilterOperator.equal: '\$eq',
|
||||
FilterOperator.notEqual: '\$ne',
|
||||
FilterOperator.greater: '\$gt',
|
||||
FilterOperator.greaterOrEqual: '\$gte',
|
||||
FilterOperator.less: '\$lt',
|
||||
FilterOperator.lessOrEqual: '\$lte',
|
||||
FilterOperator.in_: '\$in',
|
||||
FilterOperator.notIn: '\$nin',
|
||||
FilterOperator.query: '\$q',
|
||||
FilterOperator.autoComplete: '\$autocomplete',
|
||||
FilterOperator.exists: '\$exists',
|
||||
FilterOperator.and: '\$and',
|
||||
FilterOperator.or: '\$or',
|
||||
FilterOperator.nor: '\$nor',
|
||||
FilterOperator.contains: '\$contains',
|
||||
}[this]!;
|
||||
@override
|
||||
String toString() {
|
||||
switch (this) {
|
||||
case FilterOperator.equal:
|
||||
return r'$eq';
|
||||
case FilterOperator.notEqual:
|
||||
return r'$ne';
|
||||
case FilterOperator.greater:
|
||||
return r'$gt';
|
||||
case FilterOperator.greaterOrEqual:
|
||||
return r'$gte';
|
||||
case FilterOperator.less:
|
||||
return r'$lt';
|
||||
case FilterOperator.lessOrEqual:
|
||||
return r'$lte';
|
||||
case FilterOperator.in_:
|
||||
return r'$in';
|
||||
case FilterOperator.notIn:
|
||||
return r'$nin';
|
||||
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,
|
||||
@@ -96,11 +110,11 @@ class Filter extends Equatable {
|
||||
this.key,
|
||||
});
|
||||
|
||||
Filter._({
|
||||
const Filter._({
|
||||
required FilterOperator operator,
|
||||
required this.value,
|
||||
this.key,
|
||||
}) : operator = operator.rawValue;
|
||||
}) : operator = '$operator';
|
||||
|
||||
/// An empty filter
|
||||
const Filter.empty()
|
||||
@@ -215,7 +229,7 @@ class Filter extends Equatable {
|
||||
/// Serializes to json object
|
||||
Map<String, Object?> toJson() {
|
||||
final json = <String, Object?>{};
|
||||
final groupOperators = _groupOperators.map((it) => it.rawValue);
|
||||
final groupOperators = _groupOperators.map((it) => '$it');
|
||||
|
||||
if (groupOperators.contains(operator)) {
|
||||
// Filters with group operators are encoded in the following form:
|
||||
|
||||
@@ -17,34 +17,20 @@ class OwnUser extends User {
|
||||
this.totalUnreadCount = 0,
|
||||
this.unreadChannels = 0,
|
||||
this.channelMutes = const [],
|
||||
required String id,
|
||||
String? role,
|
||||
String? name,
|
||||
String? image,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
DateTime? lastActive,
|
||||
bool online = false,
|
||||
Map<String, Object?> extraData = const {},
|
||||
bool banned = false,
|
||||
DateTime? banExpires,
|
||||
List<String> teams = const [],
|
||||
String? 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,
|
||||
);
|
||||
required super.id,
|
||||
super.role,
|
||||
super.name,
|
||||
super.image,
|
||||
super.createdAt,
|
||||
super.updatedAt,
|
||||
super.lastActive,
|
||||
super.online,
|
||||
super.extraData,
|
||||
super.banned,
|
||||
super.banExpires,
|
||||
super.teams,
|
||||
super.language,
|
||||
});
|
||||
|
||||
/// Create a new instance from json.
|
||||
factory OwnUser.fromJson(Map<String, dynamic> json) => _$OwnUserFromJson(
|
||||
@@ -55,6 +41,9 @@ class OwnUser extends User {
|
||||
factory OwnUser.fromUser(User user) => OwnUser(
|
||||
id: user.id,
|
||||
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,
|
||||
updatedAt: user.updatedAt,
|
||||
lastActive: user.lastActive,
|
||||
@@ -90,10 +79,11 @@ class OwnUser extends User {
|
||||
OwnUser(
|
||||
id: id ?? this.id,
|
||||
role: role ?? this.role,
|
||||
// if null, it will be retrieved from extraData['name']
|
||||
name: name,
|
||||
// if null, it will be retrieved from extraData['image']
|
||||
image: image,
|
||||
name: name ??
|
||||
extraData?['name'] as String? ??
|
||||
// Using extraData value in order to not use id as name.
|
||||
this.extraData['name'] as String?,
|
||||
image: image ?? extraData?['image'] as String? ?? this.image,
|
||||
banned: banned ?? this.banned,
|
||||
banExpires: banExpires ?? this.banExpires,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
@@ -117,6 +107,9 @@ class OwnUser extends User {
|
||||
return copyWith(
|
||||
id: other.id,
|
||||
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,
|
||||
channelMutes: other.channelMutes,
|
||||
createdAt: other.createdAt,
|
||||
|
||||
@@ -46,6 +46,7 @@ class User extends Equatable {
|
||||
this.language,
|
||||
}) : createdAt = createdAt ?? DateTime.now(),
|
||||
updatedAt = updatedAt ?? DateTime.now(),
|
||||
// TODO: Make them top-level fields in v5
|
||||
// For backwards compatibility, set 'name', 'image' in [extraData].
|
||||
extraData = {
|
||||
...extraData,
|
||||
@@ -171,10 +172,11 @@ class User extends Equatable {
|
||||
User(
|
||||
id: id ?? this.id,
|
||||
role: role ?? this.role,
|
||||
// if null, it will be retrieved from extraData['name']
|
||||
name: name,
|
||||
// if null, it will be retrieved from extraData['image']
|
||||
image: image,
|
||||
name: name ??
|
||||
extraData?['name'] as String? ??
|
||||
// Using extraData value in order to not use id as name.
|
||||
this.extraData['name'] as String?,
|
||||
image: image ?? extraData?['image'] as String? ?? this.image,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
lastActive: lastActive ?? this.lastActive,
|
||||
|
||||
@@ -73,6 +73,9 @@ class EventType {
|
||||
/// Event sent when a member is removed to a channel
|
||||
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
|
||||
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:rxdart/rxdart.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/models/event.dart';
|
||||
import 'package:stream_chat/src/core/models/user.dart';
|
||||
@@ -163,7 +162,7 @@ class WebSocket with TimerHelper {
|
||||
'json': jsonEncode(params),
|
||||
'api_key': apiKey,
|
||||
'authorization': token.rawValue,
|
||||
'stream-auth-type': token.authType.raw,
|
||||
'stream-auth-type': token.authType.name,
|
||||
...queryParameters,
|
||||
};
|
||||
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/db/chat_persistence_client.dart';
|
||||
export './src/event_type.dart';
|
||||
export './src/location.dart';
|
||||
export './src/permission_type.dart';
|
||||
export './src/ws/connection_status.dart';
|
||||
export 'src/client/channel.dart';
|
||||
@@ -67,5 +66,4 @@ export 'src/core/models/user.dart';
|
||||
export 'src/core/util/extension.dart';
|
||||
export 'src/db/chat_persistence_client.dart';
|
||||
export 'src/event_type.dart';
|
||||
export 'src/location.dart';
|
||||
export 'src/ws/connection_status.dart';
|
||||
|
||||
@@ -3,4 +3,4 @@ import 'package:stream_chat/src/client/client.dart';
|
||||
/// Current package version
|
||||
/// Used in [StreamChatClient] to build the `x-stream-client` header
|
||||
// ignore: constant_identifier_names
|
||||
const PACKAGE_VERSION = '4.1.0';
|
||||
const PACKAGE_VERSION = '4.2.0';
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
name: stream_chat
|
||||
homepage: https://getstream.io/
|
||||
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
|
||||
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
sdk: '>=2.17.0 <3.0.0'
|
||||
|
||||
dependencies:
|
||||
async: ^2.5.0
|
||||
collection: ^1.15.0
|
||||
dio: ^4.0.0
|
||||
equatable: ^2.0.0
|
||||
freezed_annotation: ^1.0.0
|
||||
freezed_annotation: ^2.0.3
|
||||
http_parser: ^4.0.0
|
||||
jose: ^0.3.2
|
||||
json_annotation: ^4.3.0
|
||||
json_annotation: ^4.5.0
|
||||
logging: ^1.0.1
|
||||
meta: ^1.3.0
|
||||
mime: ^1.0.0
|
||||
@@ -28,7 +28,7 @@ dependencies:
|
||||
dev_dependencies:
|
||||
build_runner: ^2.0.1
|
||||
dart_code_metrics: ^4.4.0
|
||||
freezed: ^1.0.0
|
||||
json_serializable: ^6.0.1
|
||||
freezed: ^2.0.3
|
||||
json_serializable: ^6.2.0
|
||||
mocktail: ^0.3.0
|
||||
test: ^1.17.12
|
||||
@@ -11,6 +11,7 @@
|
||||
},
|
||||
"watchers": [],
|
||||
"read": [],
|
||||
"membership": null,
|
||||
"messages": [
|
||||
{
|
||||
"id": "dry-meadow-0-2b73cc8b-cd86-4a01-8d40-bd82ad07a030",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
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/models/banned_user.dart';
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
@@ -2514,7 +2513,7 @@ void main() {
|
||||
});
|
||||
|
||||
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 initialUser = OwnUser.fromUser(user);
|
||||
|
||||
@@ -46,7 +46,7 @@ void main() {
|
||||
expect(updateHeaders.containsKey('Authorization'), isTrue);
|
||||
expect(updateHeaders['Authorization'], token.rawValue);
|
||||
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['user_id'], token.userId);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ void main() {
|
||||
expect(token.userId, userId);
|
||||
expect(token.rawValue, isEmpty);
|
||||
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', () {
|
||||
@@ -36,7 +36,7 @@ void main() {
|
||||
expect(token.userId, userId);
|
||||
expect(token.rawValue, isNotEmpty);
|
||||
expect(token.authType, AuthType.jwt);
|
||||
expect(token.authType.raw, AuthType.jwt.raw);
|
||||
expect(token.authType.name, AuthType.jwt.name);
|
||||
});
|
||||
|
||||
test(
|
||||
@@ -51,7 +51,7 @@ void main() {
|
||||
expect(token.userId, user.id);
|
||||
expect(token.rawValue, isNotEmpty);
|
||||
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']),
|
||||
members: [],
|
||||
messages:
|
||||
// ignore: unnecessary_lambdas
|
||||
(j['messages'] as List).map((m) => Message.fromJson(m)).toList(),
|
||||
read: [],
|
||||
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);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, value);
|
||||
expect(filter.operator, FilterOperator.equal.rawValue);
|
||||
expect(filter.operator, FilterOperator.equal.toString());
|
||||
});
|
||||
|
||||
test('notEqual', () {
|
||||
@@ -20,7 +20,7 @@ void main() {
|
||||
final filter = Filter.notEqual(key, value);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, value);
|
||||
expect(filter.operator, FilterOperator.notEqual.rawValue);
|
||||
expect(filter.operator, FilterOperator.notEqual.toString());
|
||||
});
|
||||
|
||||
test('greater', () {
|
||||
@@ -29,7 +29,7 @@ void main() {
|
||||
final filter = Filter.greater(key, value);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, value);
|
||||
expect(filter.operator, FilterOperator.greater.rawValue);
|
||||
expect(filter.operator, FilterOperator.greater.toString());
|
||||
});
|
||||
|
||||
test('greaterOrEqual', () {
|
||||
@@ -38,7 +38,7 @@ void main() {
|
||||
final filter = Filter.greaterOrEqual(key, value);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, value);
|
||||
expect(filter.operator, FilterOperator.greaterOrEqual.rawValue);
|
||||
expect(filter.operator, FilterOperator.greaterOrEqual.toString());
|
||||
});
|
||||
|
||||
test('less', () {
|
||||
@@ -47,7 +47,7 @@ void main() {
|
||||
final filter = Filter.less(key, value);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, value);
|
||||
expect(filter.operator, FilterOperator.less.rawValue);
|
||||
expect(filter.operator, FilterOperator.less.toString());
|
||||
});
|
||||
|
||||
test('lessOrEqual', () {
|
||||
@@ -56,7 +56,7 @@ void main() {
|
||||
final filter = Filter.lessOrEqual(key, value);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, value);
|
||||
expect(filter.operator, FilterOperator.lessOrEqual.rawValue);
|
||||
expect(filter.operator, FilterOperator.lessOrEqual.toString());
|
||||
});
|
||||
|
||||
test('in', () {
|
||||
@@ -65,7 +65,7 @@ void main() {
|
||||
final filter = Filter.in_(key, values);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, values);
|
||||
expect(filter.operator, FilterOperator.in_.rawValue);
|
||||
expect(filter.operator, FilterOperator.in_.toString());
|
||||
});
|
||||
|
||||
test('in', () {
|
||||
@@ -74,7 +74,7 @@ void main() {
|
||||
final filter = Filter.in_(key, values);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, values);
|
||||
expect(filter.operator, FilterOperator.in_.rawValue);
|
||||
expect(filter.operator, FilterOperator.in_.toString());
|
||||
});
|
||||
|
||||
test('notIn', () {
|
||||
@@ -83,7 +83,7 @@ void main() {
|
||||
final filter = Filter.notIn(key, values);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, values);
|
||||
expect(filter.operator, FilterOperator.notIn.rawValue);
|
||||
expect(filter.operator, FilterOperator.notIn.toString());
|
||||
});
|
||||
|
||||
test('query', () {
|
||||
@@ -92,7 +92,7 @@ void main() {
|
||||
final filter = Filter.query(key, value);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, value);
|
||||
expect(filter.operator, FilterOperator.query.rawValue);
|
||||
expect(filter.operator, FilterOperator.query.toString());
|
||||
});
|
||||
|
||||
test('autoComplete', () {
|
||||
@@ -101,7 +101,7 @@ void main() {
|
||||
final filter = Filter.autoComplete(key, value);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, value);
|
||||
expect(filter.operator, FilterOperator.autoComplete.rawValue);
|
||||
expect(filter.operator, FilterOperator.autoComplete.toString());
|
||||
});
|
||||
|
||||
test('exists', () {
|
||||
@@ -109,7 +109,7 @@ void main() {
|
||||
final filter = Filter.exists(key);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, isTrue);
|
||||
expect(filter.operator, FilterOperator.exists.rawValue);
|
||||
expect(filter.operator, FilterOperator.exists.toString());
|
||||
});
|
||||
|
||||
test('notExists', () {
|
||||
@@ -117,13 +117,13 @@ void main() {
|
||||
final filter = Filter.notExists(key);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, isFalse);
|
||||
expect(filter.operator, FilterOperator.exists.rawValue);
|
||||
expect(filter.operator, FilterOperator.exists.toString());
|
||||
});
|
||||
|
||||
test('custom', () {
|
||||
const key = 'testKey';
|
||||
const value = 'testValue';
|
||||
const operator = '\$customOperator';
|
||||
const operator = r'$customOperator';
|
||||
const filter = Filter.custom(operator: operator, key: key, value: value);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, value);
|
||||
@@ -149,7 +149,7 @@ void main() {
|
||||
final filter = Filter.contains(key, values);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, values);
|
||||
expect(filter.operator, FilterOperator.contains.rawValue);
|
||||
expect(filter.operator, FilterOperator.contains.toString());
|
||||
});
|
||||
|
||||
group('groupedOperator', () {
|
||||
@@ -161,21 +161,21 @@ void main() {
|
||||
final filter = Filter.and(filters);
|
||||
expect(filter.key, isNull);
|
||||
expect(filter.value, filters);
|
||||
expect(filter.operator, FilterOperator.and.rawValue);
|
||||
expect(filter.operator, FilterOperator.and.toString());
|
||||
});
|
||||
|
||||
test('or', () {
|
||||
final filter = Filter.or(filters);
|
||||
expect(filter.key, isNull);
|
||||
expect(filter.value, filters);
|
||||
expect(filter.operator, FilterOperator.or.rawValue);
|
||||
expect(filter.operator, FilterOperator.or.toString());
|
||||
});
|
||||
|
||||
test('nor', () {
|
||||
final filter = Filter.nor(filters);
|
||||
expect(filter.key, isNull);
|
||||
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);
|
||||
expect(
|
||||
encoded,
|
||||
'{"$key":{"${FilterOperator.equal.rawValue}":${json.encode(value)}}}',
|
||||
'''{"$key":{"${FilterOperator.equal.toString()}":${json.encode(value)}}}''',
|
||||
);
|
||||
});
|
||||
test('listValue', () {
|
||||
@@ -199,7 +199,7 @@ void main() {
|
||||
final encoded = json.encode(filter);
|
||||
expect(
|
||||
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);
|
||||
expect(
|
||||
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
|
||||
|
||||
✅ Added
|
||||
@@ -8,7 +14,9 @@
|
||||
🐞 Fixed
|
||||
|
||||
- Fixed attachment picker ui.
|
||||
- Fixed StreamChannelHeader and StreamThreadHeader subtitle alignment.
|
||||
- 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
|
||||
|
||||
|
||||
@@ -47,10 +47,10 @@ class MyApp extends StatelessWidget {
|
||||
/// If you'd prefer using minimal wrapper widgets for your app, please see
|
||||
/// our other package, `stream_chat_flutter_core`.
|
||||
const MyApp({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.client,
|
||||
required this.channel,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Instance of Stream Client.
|
||||
///
|
||||
@@ -94,8 +94,8 @@ class MyApp extends StatelessWidget {
|
||||
class ChannelPage extends StatelessWidget {
|
||||
/// Creates the page that shows the list of messages
|
||||
const ChannelPage({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
|
||||
@@ -22,9 +22,9 @@ void main() async {
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.client,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final StreamChatClient client;
|
||||
|
||||
@@ -40,8 +40,8 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
class SplitView extends StatefulWidget {
|
||||
const SplitView({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
_SplitViewState createState() => _SplitViewState();
|
||||
@@ -86,9 +86,9 @@ class _SplitViewState extends State<SplitView> {
|
||||
|
||||
class ChannelListPage extends StatefulWidget {
|
||||
const ChannelListPage({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.onTap,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final void Function(Channel)? onTap;
|
||||
|
||||
@@ -118,8 +118,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
||||
|
||||
class ChannelPage extends StatelessWidget {
|
||||
const ChannelPage({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Navigator(
|
||||
|
||||
@@ -58,10 +58,10 @@ void main() async {
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.client,
|
||||
required this.channel,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final StreamChatClient client;
|
||||
|
||||
@@ -86,8 +86,8 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
class ChannelPage extends StatelessWidget {
|
||||
const ChannelPage({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
|
||||
@@ -51,9 +51,9 @@ void main() async {
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.client,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final StreamChatClient client;
|
||||
|
||||
@@ -73,9 +73,9 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
class ChannelListPage extends StatefulWidget {
|
||||
const ChannelListPage({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.client,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final StreamChatClient client;
|
||||
|
||||
@@ -121,8 +121,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
||||
|
||||
class ChannelPage extends StatelessWidget {
|
||||
const ChannelPage({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
|
||||
@@ -51,9 +51,9 @@ Future<void> main() async {
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.client,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final StreamChatClient client;
|
||||
|
||||
@@ -71,8 +71,8 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
class ChannelListPage extends StatefulWidget {
|
||||
const ChannelListPage({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ChannelListPage> createState() => _ChannelListPageState();
|
||||
@@ -164,8 +164,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
||||
|
||||
class ChannelPage extends StatelessWidget {
|
||||
const ChannelPage({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -38,9 +38,9 @@ Future<void> main() async {
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.client,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final StreamChatClient client;
|
||||
|
||||
@@ -58,8 +58,8 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
class ChannelListPage extends StatefulWidget {
|
||||
const ChannelListPage({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ChannelListPage> createState() => _ChannelListPageState();
|
||||
@@ -102,8 +102,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
||||
|
||||
class ChannelPage extends StatelessWidget {
|
||||
const ChannelPage({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
@@ -125,9 +125,9 @@ class ChannelPage extends StatelessWidget {
|
||||
|
||||
class ThreadPage extends StatelessWidget {
|
||||
const ThreadPage({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.parent,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final Message? parent;
|
||||
|
||||
|
||||
@@ -41,9 +41,9 @@ Future<void> main() async {
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.client,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final StreamChatClient client;
|
||||
|
||||
@@ -61,8 +61,8 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
class ChannelListPage extends StatefulWidget {
|
||||
const ChannelListPage({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ChannelListPage> createState() => _ChannelListPageState();
|
||||
@@ -105,8 +105,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
||||
|
||||
class ChannelPage extends StatelessWidget {
|
||||
const ChannelPage({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -139,7 +139,7 @@ class ChannelPage extends StatelessWidget {
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(5),
|
||||
child: Container(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: color,
|
||||
|
||||
@@ -48,9 +48,9 @@ Future<void> main() async {
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.client,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final StreamChatClient client;
|
||||
|
||||
@@ -101,8 +101,8 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
class ChannelListPage extends StatefulWidget {
|
||||
const ChannelListPage({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ChannelListPage> createState() => _ChannelListPageState();
|
||||
@@ -145,8 +145,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
||||
|
||||
class ChannelPage extends StatelessWidget {
|
||||
const ChannelPage({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
@@ -168,9 +168,9 @@ class ChannelPage extends StatelessWidget {
|
||||
|
||||
class ThreadPage extends StatelessWidget {
|
||||
const ThreadPage({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.parent,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
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
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
sdk: '>=2.17.0 <3.0.0'
|
||||
|
||||
dependencies:
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
|
||||
+1
-1
@@ -320,7 +320,7 @@ class _PositionedListState extends State<PositionedList> {
|
||||
void _schedulePositionNotificationUpdate() {
|
||||
if (!updateScheduled) {
|
||||
updateScheduled = true;
|
||||
SchedulerBinding.instance!.addPostFrameCallback((_) {
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
if (registeredElements.value == null) {
|
||||
updateScheduled = false;
|
||||
return;
|
||||
|
||||
+2
-2
@@ -439,7 +439,7 @@ class _ScrollablePositionedListState extends State<ScrollablePositionedList>
|
||||
}
|
||||
if (_isTransitioning) {
|
||||
_stopScroll(canceled: true);
|
||||
SchedulerBinding.instance!.addPostFrameCallback((_) {
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
_startScroll(
|
||||
index: index,
|
||||
alignment: alignment,
|
||||
@@ -488,7 +488,7 @@ class _ScrollablePositionedListState extends State<ScrollablePositionedList>
|
||||
final startCompleter = Completer<void>();
|
||||
final endCompleter = Completer<void>();
|
||||
startAnimationCallback = () {
|
||||
SchedulerBinding.instance!.addPostFrameCallback((_) {
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
startAnimationCallback = () {};
|
||||
|
||||
opacity.parent = _opacityAnimation(opacityAnimationWeights).animate(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export 'attachment_upload_state_builder.dart';
|
||||
export 'attachment_widget.dart'
|
||||
show AttachmentError, AttachmentSource, AttachmentSourceX;
|
||||
export 'attachment_widget.dart' show AttachmentError, AttachmentSource;
|
||||
export 'file_attachment.dart';
|
||||
export 'giphy_attachment.dart';
|
||||
export 'image_attachment.dart';
|
||||
|
||||
@@ -11,10 +11,10 @@ typedef AttachmentTitle = StreamAttachmentTitle;
|
||||
class StreamAttachmentTitle extends StatelessWidget {
|
||||
/// Supply attachment and theme for constructing title
|
||||
const StreamAttachmentTitle({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.attachment,
|
||||
required this.messageTheme,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Theme to apply to text
|
||||
final StreamMessageThemeData messageTheme;
|
||||
|
||||
+10
-21
@@ -19,14 +19,14 @@ typedef AttachmentUploadStateBuilder = StreamAttachmentUploadStateBuilder;
|
||||
class StreamAttachmentUploadStateBuilder extends StatelessWidget {
|
||||
/// Constructor for creating an [StreamAttachmentUploadStateBuilder] widget
|
||||
const StreamAttachmentUploadStateBuilder({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.message,
|
||||
required this.attachment,
|
||||
this.failedBuilder,
|
||||
this.successBuilder,
|
||||
this.inProgressBuilder,
|
||||
this.preparingBuilder,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Message which attachment is added to
|
||||
final Message message;
|
||||
@@ -85,30 +85,24 @@ class StreamAttachmentUploadStateBuilder extends StatelessWidget {
|
||||
|
||||
class _IconButton extends StatelessWidget {
|
||||
const _IconButton({
|
||||
Key? key,
|
||||
this.icon,
|
||||
this.iconSize = 24.0,
|
||||
this.onPressed,
|
||||
this.fillColor,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final Widget? icon;
|
||||
final double iconSize;
|
||||
final VoidCallback? onPressed;
|
||||
final Color? fillColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => SizedBox(
|
||||
height: iconSize,
|
||||
width: iconSize,
|
||||
height: 24,
|
||||
width: 24,
|
||||
child: RawMaterialButton(
|
||||
elevation: 0,
|
||||
highlightElevation: 0,
|
||||
focusElevation: 0,
|
||||
hoverElevation: 0,
|
||||
onPressed: onPressed,
|
||||
fillColor:
|
||||
fillColor ?? StreamChatTheme.of(context).colorTheme.overlayDark,
|
||||
fillColor: StreamChatTheme.of(context).colorTheme.overlayDark,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
@@ -118,10 +112,7 @@ class _IconButton extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _PreparingState extends StatelessWidget {
|
||||
const _PreparingState({
|
||||
Key? key,
|
||||
required this.attachmentId,
|
||||
}) : super(key: key);
|
||||
const _PreparingState({required this.attachmentId});
|
||||
|
||||
final String attachmentId;
|
||||
|
||||
@@ -155,11 +146,10 @@ class _PreparingState extends StatelessWidget {
|
||||
|
||||
class _InProgressState extends StatelessWidget {
|
||||
const _InProgressState({
|
||||
Key? key,
|
||||
required this.sent,
|
||||
required this.total,
|
||||
required this.attachmentId,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final int sent;
|
||||
final int total;
|
||||
@@ -195,11 +185,10 @@ class _InProgressState extends StatelessWidget {
|
||||
|
||||
class _FailedState extends StatelessWidget {
|
||||
const _FailedState({
|
||||
Key? key,
|
||||
this.error,
|
||||
required this.messageId,
|
||||
required this.attachmentId,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final String? error;
|
||||
final String messageId;
|
||||
@@ -222,7 +211,7 @@ class _FailedState extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
Center(
|
||||
child: Container(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: theme.colorTheme.overlayDark.withOpacity(0.6),
|
||||
|
||||
@@ -7,14 +7,10 @@ enum AttachmentSource {
|
||||
local,
|
||||
|
||||
/// Attachment is uploaded
|
||||
network,
|
||||
}
|
||||
network;
|
||||
|
||||
/// Extension for identifying type of attachment
|
||||
extension AttachmentSourceX on AttachmentSource {
|
||||
/// The [when] method is the equivalent to pattern matching.
|
||||
/// Its prototype depends on the AttachmentSource defined.
|
||||
// ignore: missing_return
|
||||
T when<T>({
|
||||
required T Function() local,
|
||||
required T Function() network,
|
||||
@@ -38,13 +34,12 @@ typedef AttachmentWidget = StreamAttachmentWidget;
|
||||
abstract class StreamAttachmentWidget extends StatelessWidget {
|
||||
/// Constructor for creating attachment widget
|
||||
const StreamAttachmentWidget({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.message,
|
||||
required this.attachment,
|
||||
this.size,
|
||||
AttachmentSource? source,
|
||||
}) : _source = source,
|
||||
super(key: key);
|
||||
}) : _source = source;
|
||||
|
||||
/// Size of attachments
|
||||
final Size? size;
|
||||
@@ -68,9 +63,9 @@ abstract class StreamAttachmentWidget extends StatelessWidget {
|
||||
class AttachmentError extends StatelessWidget {
|
||||
/// Constructor for creating AttachmentError
|
||||
const AttachmentError({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.size,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Size of error
|
||||
final Size? size;
|
||||
|
||||
@@ -20,19 +20,14 @@ typedef FileAttachment = StreamFileAttachment;
|
||||
class StreamFileAttachment extends StreamAttachmentWidget {
|
||||
/// Constructor for creating a widget when attachment is of type 'file'
|
||||
const StreamFileAttachment({
|
||||
Key? key,
|
||||
required Message message,
|
||||
required Attachment attachment,
|
||||
Size? size,
|
||||
super.key,
|
||||
required super.message,
|
||||
required super.attachment,
|
||||
super.size,
|
||||
this.title,
|
||||
this.trailing,
|
||||
this.onAttachmentTap,
|
||||
}) : super(
|
||||
key: key,
|
||||
message: message,
|
||||
attachment: attachment,
|
||||
size: size,
|
||||
);
|
||||
});
|
||||
|
||||
/// Title for attachment
|
||||
final Widget? title;
|
||||
|
||||
@@ -15,19 +15,14 @@ typedef GiphyAttachment = StreamGiphyAttachment;
|
||||
class StreamGiphyAttachment extends StreamAttachmentWidget {
|
||||
/// Constructor for creating a [StreamGiphyAttachment] widget
|
||||
const StreamGiphyAttachment({
|
||||
Key? key,
|
||||
required Message message,
|
||||
required Attachment attachment,
|
||||
Size? size,
|
||||
super.key,
|
||||
required super.message,
|
||||
required super.attachment,
|
||||
super.size,
|
||||
this.onShowMessage,
|
||||
this.onReturnAction,
|
||||
this.onAttachmentTap,
|
||||
}) : super(
|
||||
key: key,
|
||||
message: message,
|
||||
attachment: attachment,
|
||||
size: size,
|
||||
);
|
||||
});
|
||||
|
||||
/// Callback when show message is tapped
|
||||
final ShowMessageCallback? onShowMessage;
|
||||
|
||||
@@ -15,21 +15,16 @@ typedef ImageAttachment = StreamImageAttachment;
|
||||
class StreamImageAttachment extends StreamAttachmentWidget {
|
||||
/// Constructor for creating a [StreamImageAttachment] widget
|
||||
const StreamImageAttachment({
|
||||
Key? key,
|
||||
required Message message,
|
||||
required Attachment attachment,
|
||||
super.key,
|
||||
required super.message,
|
||||
required super.attachment,
|
||||
required this.messageTheme,
|
||||
Size? size,
|
||||
super.size,
|
||||
this.showTitle = false,
|
||||
this.onShowMessage,
|
||||
this.onReturnAction,
|
||||
this.onAttachmentTap,
|
||||
}) : super(
|
||||
key: key,
|
||||
message: message,
|
||||
attachment: attachment,
|
||||
size: size,
|
||||
);
|
||||
});
|
||||
|
||||
/// [StreamMessageThemeData] for showing image title
|
||||
final StreamMessageThemeData messageTheme;
|
||||
|
||||
@@ -12,7 +12,7 @@ typedef UrlAttachment = StreamUrlAttachment;
|
||||
class StreamUrlAttachment extends StatelessWidget {
|
||||
/// Constructor for creating a [StreamUrlAttachment]
|
||||
const StreamUrlAttachment({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.urlAttachment,
|
||||
required this.hostDisplayName,
|
||||
required this.messageTheme,
|
||||
@@ -21,7 +21,7 @@ class StreamUrlAttachment extends StatelessWidget {
|
||||
vertical: 8,
|
||||
),
|
||||
this.onLinkTap,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Attachment to be displayed
|
||||
final Attachment urlAttachment;
|
||||
@@ -70,7 +70,7 @@ class StreamUrlAttachment extends StatelessWidget {
|
||||
Positioned(
|
||||
left: 0,
|
||||
bottom: -1,
|
||||
child: Container(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topRight: Radius.circular(16),
|
||||
|
||||
@@ -14,20 +14,15 @@ typedef VideoAttachment = StreamVideoAttachment;
|
||||
class StreamVideoAttachment extends StreamAttachmentWidget {
|
||||
/// Constructor for creating a [StreamVideoAttachment] widget
|
||||
const StreamVideoAttachment({
|
||||
Key? key,
|
||||
required Message message,
|
||||
required Attachment attachment,
|
||||
super.key,
|
||||
required super.message,
|
||||
required super.attachment,
|
||||
required this.messageTheme,
|
||||
Size? size,
|
||||
super.size,
|
||||
this.onShowMessage,
|
||||
this.onReturnAction,
|
||||
this.onAttachmentTap,
|
||||
}) : super(
|
||||
key: key,
|
||||
message: message,
|
||||
attachment: attachment,
|
||||
size: size,
|
||||
);
|
||||
});
|
||||
|
||||
/// [StreamMessageThemeData] for showing title
|
||||
final StreamMessageThemeData messageTheme;
|
||||
|
||||
@@ -19,7 +19,7 @@ typedef DownloadedPathCallback = void Function(String? path);
|
||||
class AttachmentActionsModal extends StatelessWidget {
|
||||
/// Returns a new [AttachmentActionsModal]
|
||||
const AttachmentActionsModal({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.attachment,
|
||||
required this.message,
|
||||
this.onShowMessage,
|
||||
@@ -30,7 +30,7 @@ class AttachmentActionsModal extends StatelessWidget {
|
||||
this.showSave = true,
|
||||
this.showDelete = true,
|
||||
this.customActions = const [],
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// The attachment object for which the actions are to be performed
|
||||
final Attachment attachment;
|
||||
|
||||
@@ -6,11 +6,11 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
class StreamBackButton extends StatelessWidget {
|
||||
/// Constructor for creating back button
|
||||
const StreamBackButton({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.onPressed,
|
||||
this.showUnreads = false,
|
||||
this.cid,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Callback for when button is pressed
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
@@ -52,7 +52,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
class ChannelAvatar extends StatelessWidget {
|
||||
/// Instantiate a new ChannelImage
|
||||
const ChannelAvatar({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.channel,
|
||||
this.constraints,
|
||||
this.onTap,
|
||||
@@ -60,7 +60,7 @@ class ChannelAvatar extends StatelessWidget {
|
||||
this.selected = false,
|
||||
this.selectionColor,
|
||||
this.selectionThickness = 4,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// [BorderRadius] to display the widget
|
||||
final BorderRadius? borderRadius;
|
||||
|
||||
@@ -7,7 +7,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
@Deprecated("Use 'StreamChannelInfoBottomSheet' instead")
|
||||
class ChannelBottomSheet extends StatefulWidget {
|
||||
/// 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
|
||||
final VoidCallback? onViewInfoTap;
|
||||
|
||||
@@ -60,7 +60,7 @@ class StreamChannelHeader extends StatelessWidget
|
||||
implements PreferredSizeWidget {
|
||||
/// Creates a channel header
|
||||
const StreamChannelHeader({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.showBackButton = true,
|
||||
this.onBackPressed,
|
||||
this.onTitleTap,
|
||||
@@ -74,8 +74,7 @@ class StreamChannelHeader extends StatelessWidget
|
||||
this.actions,
|
||||
this.backgroundColor,
|
||||
this.elevation = 1,
|
||||
}) : preferredSize = const Size.fromHeight(kToolbarHeight),
|
||||
super(key: key);
|
||||
}) : preferredSize = const Size.fromHeight(kToolbarHeight);
|
||||
|
||||
/// True if this header shows the leading back button
|
||||
final bool showBackButton;
|
||||
|
||||
@@ -13,12 +13,12 @@ typedef ChannelInfo = StreamChannelInfo;
|
||||
class StreamChannelInfo extends StatelessWidget {
|
||||
/// Constructor which creates a [StreamChannelInfo] widget
|
||||
const StreamChannelInfo({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.channel,
|
||||
this.textStyle,
|
||||
this.showTypingIndicator = true,
|
||||
this.parentId,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// The channel about which the info is to be displayed
|
||||
final Channel channel;
|
||||
@@ -100,12 +100,10 @@ class StreamChannelInfo extends StatelessWidget {
|
||||
return alternativeWidget ?? const Offstage();
|
||||
}
|
||||
|
||||
return Align(
|
||||
child: StreamTypingIndicator(
|
||||
parentId: parentId,
|
||||
style: textStyle,
|
||||
alternativeWidget: alternativeWidget,
|
||||
),
|
||||
return StreamTypingIndicator(
|
||||
parentId: parentId,
|
||||
style: textStyle,
|
||||
alternativeWidget: alternativeWidget,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -140,7 +138,7 @@ class StreamChannelInfo extends StatelessWidget {
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.all(0),
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
visualDensity: const VisualDensity(
|
||||
horizontal: VisualDensity.minimumDensity,
|
||||
|
||||
@@ -54,7 +54,7 @@ class StreamChannelListHeader extends StatelessWidget
|
||||
implements PreferredSizeWidget {
|
||||
/// Instantiates a ChannelListHeader
|
||||
const StreamChannelListHeader({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.client,
|
||||
this.titleBuilder,
|
||||
this.onUserAvatarTap,
|
||||
@@ -67,7 +67,7 @@ class StreamChannelListHeader extends StatelessWidget
|
||||
this.actions,
|
||||
this.backgroundColor,
|
||||
this.elevation = 1,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Pass this if you don't have a [StreamChatClient] in your widget tree.
|
||||
final StreamChatClient? client;
|
||||
@@ -155,9 +155,7 @@ class StreamChannelListHeader extends StatelessWidget
|
||||
showOnlineStatus: false,
|
||||
onTap: onUserAvatarTap ??
|
||||
(_) {
|
||||
if (preNavigationCallback != null) {
|
||||
preNavigationCallback!();
|
||||
}
|
||||
preNavigationCallback?.call();
|
||||
Scaffold.of(context).openDrawer();
|
||||
},
|
||||
borderRadius: channelListHeaderThemeData
|
||||
|
||||
@@ -57,8 +57,9 @@ typedef ViewInfoCallback = void Function(Channel);
|
||||
@Deprecated("Use 'StreamChannelListView' instead")
|
||||
class ChannelListView extends StatefulWidget {
|
||||
/// Instantiate a new ChannelListView
|
||||
@Deprecated("Use 'StreamChannelListView' instead")
|
||||
ChannelListView({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.filter,
|
||||
this.sort,
|
||||
this.state = true,
|
||||
@@ -93,8 +94,7 @@ class ChannelListView extends StatefulWidget {
|
||||
this.onDeletePressed,
|
||||
this.swipeActions,
|
||||
this.channelListController,
|
||||
}) : limit = limit ?? pagination?.limit ?? 25,
|
||||
super(key: key);
|
||||
}) : limit = limit ?? pagination?.limit ?? 25;
|
||||
|
||||
/// If true a default swipe to action behaviour will be added to this widget
|
||||
final bool swipeToAction;
|
||||
@@ -688,7 +688,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
||||
initialData: false,
|
||||
errorBuilder: (context, err) {
|
||||
final theme = StreamChatTheme.of(context);
|
||||
return Container(
|
||||
return ColoredBox(
|
||||
color: theme.colorTheme.textLowEmphasis.withOpacity(0.9),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
|
||||
@@ -10,10 +10,10 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
class ChannelName extends StatelessWidget {
|
||||
/// Instantiate a new ChannelName
|
||||
const ChannelName({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.textStyle,
|
||||
this.textOverflow = TextOverflow.ellipsis,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// The style of the text displayed
|
||||
final TextStyle? textStyle;
|
||||
|
||||
@@ -25,7 +25,7 @@ class ChannelPreview extends StatelessWidget {
|
||||
/// Constructor for creating [ChannelPreview]
|
||||
const ChannelPreview({
|
||||
required this.channel,
|
||||
Key? key,
|
||||
super.key,
|
||||
this.onTap,
|
||||
this.onLongPress,
|
||||
this.onImageTap,
|
||||
@@ -34,7 +34,7 @@ class ChannelPreview extends StatelessWidget {
|
||||
this.leading,
|
||||
this.sendingIndicator,
|
||||
this.trailing,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Function called when tapping this widget
|
||||
final void Function(Channel)? onTap;
|
||||
|
||||
@@ -17,8 +17,8 @@ class StreamCommandsOverlay extends StatelessWidget {
|
||||
required this.onCommandResult,
|
||||
required this.size,
|
||||
required this.channel,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// The size of the overlay
|
||||
final Size size;
|
||||
@@ -60,7 +60,7 @@ class StreamCommandsOverlay extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(0),
|
||||
padding: EdgeInsets.zero,
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
if (commands.isNotEmpty)
|
||||
|
||||
@@ -15,12 +15,12 @@ typedef ConnectionStatusBuilder = StreamConnectionStatusBuilder;
|
||||
class StreamConnectionStatusBuilder extends StatelessWidget {
|
||||
/// Creates a new ConnectionStatusBuilder
|
||||
const StreamConnectionStatusBuilder({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.statusBuilder,
|
||||
this.connectionStatusStream,
|
||||
this.errorBuilder,
|
||||
this.loadingBuilder,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// The asynchronous computation to which this builder is currently connected.
|
||||
final Stream<ConnectionStatus>? connectionStatusStream;
|
||||
|
||||
@@ -13,10 +13,10 @@ typedef DateDivider = StreamDateDivider;
|
||||
class StreamDateDivider extends StatelessWidget {
|
||||
/// Constructor for creating a [StreamDateDivider]
|
||||
const StreamDateDivider({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.dateTime,
|
||||
this.uppercase = false,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// [DateTime] to display
|
||||
final DateTime dateTime;
|
||||
|
||||
@@ -13,13 +13,13 @@ typedef DeletedMessage = StreamDeletedMessage;
|
||||
class StreamDeletedMessage extends StatelessWidget {
|
||||
/// Constructor to create [StreamDeletedMessage]
|
||||
const StreamDeletedMessage({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.messageTheme,
|
||||
this.borderRadiusGeometry,
|
||||
this.shape,
|
||||
this.borderSide,
|
||||
this.reverse = false,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// The theme of the message
|
||||
final StreamMessageThemeData messageTheme;
|
||||
|
||||
@@ -17,8 +17,8 @@ class StreamEmojiOverlay extends StatelessWidget {
|
||||
required this.query,
|
||||
required this.onEmojiResult,
|
||||
required this.size,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// The size of the overlay
|
||||
final Size size;
|
||||
@@ -65,7 +65,7 @@ class StreamEmojiOverlay extends StatelessWidget {
|
||||
color: _streamChatTheme.colorTheme.barsBg,
|
||||
),
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.all(0),
|
||||
padding: EdgeInsets.zero,
|
||||
shrinkWrap: true,
|
||||
itemCount: emojis.length + 1,
|
||||
itemBuilder: (context, i) {
|
||||
|
||||
@@ -31,15 +31,14 @@ typedef FullScreenMedia = StreamFullScreenMedia;
|
||||
class StreamFullScreenMedia extends StatefulWidget {
|
||||
/// Instantiate a new FullScreenImage
|
||||
const StreamFullScreenMedia({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.mediaAttachmentPackages,
|
||||
this.startIndex = 0,
|
||||
String? userName,
|
||||
this.onShowMessage,
|
||||
this.attachmentActionsModalBuilder,
|
||||
this.autoplayVideos = false,
|
||||
}) : userName = userName ?? '',
|
||||
super(key: key);
|
||||
}) : userName = userName ?? '';
|
||||
|
||||
/// The url of the image
|
||||
final List<StreamAttachmentPackage> mediaAttachmentPackages;
|
||||
|
||||
@@ -20,7 +20,7 @@ class StreamGalleryFooter extends StatefulWidget
|
||||
implements PreferredSizeWidget {
|
||||
/// Creates a StreamGalleryFooter
|
||||
const StreamGalleryFooter({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.onBackPressed,
|
||||
this.onTitleTap,
|
||||
this.onImageTap,
|
||||
@@ -29,8 +29,7 @@ class StreamGalleryFooter extends StatefulWidget
|
||||
required this.mediaAttachmentPackages,
|
||||
this.mediaSelectedCallBack,
|
||||
this.backgroundColor,
|
||||
}) : preferredSize = const Size.fromHeight(kToolbarHeight),
|
||||
super(key: key);
|
||||
}) : preferredSize = const Size.fromHeight(kToolbarHeight);
|
||||
|
||||
/// Callback to call when pressing the back button.
|
||||
/// By default it calls [Navigator.pop]
|
||||
|
||||
@@ -26,7 +26,7 @@ class StreamGalleryHeader extends StatelessWidget
|
||||
implements PreferredSizeWidget {
|
||||
/// Creates a channel header
|
||||
const StreamGalleryHeader({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.message,
|
||||
required this.attachment,
|
||||
this.showBackButton = true,
|
||||
@@ -38,8 +38,7 @@ class StreamGalleryHeader extends StatelessWidget
|
||||
this.sentAt = '',
|
||||
this.backgroundColor,
|
||||
this.attachmentActionsModalBuilder,
|
||||
}) : preferredSize = const Size.fromHeight(kToolbarHeight),
|
||||
super(key: key);
|
||||
}) : preferredSize = const Size.fromHeight(kToolbarHeight);
|
||||
|
||||
/// True if this header shows the leading back button
|
||||
final bool showBackButton;
|
||||
|
||||
@@ -13,10 +13,10 @@ typedef GradientAvatar = StreamGradientAvatar;
|
||||
class StreamGradientAvatar extends StatefulWidget {
|
||||
/// Constructor for [StreamGradientAvatar]
|
||||
const StreamGradientAvatar({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.name,
|
||||
required this.userId,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Name of user to shorten and display
|
||||
final String name;
|
||||
|
||||
@@ -11,7 +11,7 @@ typedef GroupAvatar = StreamGroupAvatar;
|
||||
class StreamGroupAvatar extends StatelessWidget {
|
||||
/// Constructor for creating a [StreamGroupAvatar]
|
||||
const StreamGroupAvatar({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.channel,
|
||||
required this.members,
|
||||
this.constraints,
|
||||
@@ -20,7 +20,7 @@ class StreamGroupAvatar extends StatelessWidget {
|
||||
this.selected = false,
|
||||
this.selectionColor,
|
||||
this.selectionThickness = 4,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// The channel of the avatar
|
||||
final Channel? channel;
|
||||
|
||||
@@ -11,7 +11,7 @@ typedef ImageGroup = StreamImageGroup;
|
||||
class StreamImageGroup extends StatelessWidget {
|
||||
/// Constructor for creating [StreamImageGroup] widget
|
||||
const StreamImageGroup({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.images,
|
||||
required this.message,
|
||||
required this.messageTheme,
|
||||
@@ -19,7 +19,7 @@ class StreamImageGroup extends StatelessWidget {
|
||||
this.onReturnAction,
|
||||
this.onShowMessage,
|
||||
this.onAttachmentTap,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// List of attachments to show
|
||||
final List<Attachment> images;
|
||||
|
||||
@@ -12,7 +12,7 @@ typedef InfoTile = StreamInfoTile;
|
||||
class StreamInfoTile extends StatelessWidget {
|
||||
/// Constructor for creating an [StreamInfoTile] widget
|
||||
const StreamInfoTile({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.message,
|
||||
required this.child,
|
||||
required this.showMessage,
|
||||
@@ -20,7 +20,7 @@ class StreamInfoTile extends StatelessWidget {
|
||||
this.childAnchor,
|
||||
this.textStyle,
|
||||
this.backgroundColor,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// String to display
|
||||
final String message;
|
||||
|
||||
@@ -391,7 +391,7 @@ class DefaultTranslations implements Translations {
|
||||
|
||||
@override
|
||||
String get sendMessagePermissionError =>
|
||||
'You don\'t have permission to send messages';
|
||||
"You don't have permission to send messages";
|
||||
|
||||
@override
|
||||
String get emptyMessagesText => 'There are no messages currently';
|
||||
@@ -528,7 +528,7 @@ class DefaultTranslations implements Translations {
|
||||
|
||||
@override
|
||||
String get operationCouldNotBeCompletedText =>
|
||||
'The operation couldn\'t be completed.';
|
||||
"The operation couldn't be completed.";
|
||||
|
||||
@override
|
||||
String get replyLabel => 'Reply';
|
||||
|
||||
@@ -18,11 +18,11 @@ typedef MediaListView = StreamMediaListView;
|
||||
class StreamMediaListView extends StatefulWidget {
|
||||
/// Constructor for creating a [StreamMediaListView] widget
|
||||
const StreamMediaListView({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.selectedIds = const [],
|
||||
this.onSelect,
|
||||
this.controller,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Stores the media selected
|
||||
final List<String> selectedIds;
|
||||
@@ -67,11 +67,9 @@ class _StreamMediaListViewState extends State<StreamMediaListView> {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 1, vertical: 1),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (widget.onSelect != null) {
|
||||
widget.onSelect!(media);
|
||||
}
|
||||
},
|
||||
onTap: widget.onSelect == null
|
||||
? null
|
||||
: () => widget.onSelect!(media),
|
||||
child: Stack(
|
||||
children: [
|
||||
AspectRatio(
|
||||
|
||||
@@ -13,7 +13,7 @@ typedef MessageActionsModal = StreamMessageActionsModal;
|
||||
class StreamMessageActionsModal extends StatefulWidget {
|
||||
/// Constructor for creating a [StreamMessageActionsModal] widget
|
||||
const StreamMessageActionsModal({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.message,
|
||||
required this.messageWidget,
|
||||
required this.messageTheme,
|
||||
@@ -32,7 +32,7 @@ class StreamMessageActionsModal extends StatefulWidget {
|
||||
this.reverse = false,
|
||||
this.customActions = const [],
|
||||
this.onCopyTap,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Widget that shows the message
|
||||
final Widget messageWidget;
|
||||
@@ -252,7 +252,7 @@ class _StreamMessageActionsModalState extends State<StreamMessageActionsModal> {
|
||||
sigmaX: 10,
|
||||
sigmaY: 10,
|
||||
),
|
||||
child: Container(
|
||||
child: ColoredBox(
|
||||
color: streamChatThemeData.colorTheme.overlay,
|
||||
),
|
||||
),
|
||||
@@ -406,9 +406,7 @@ class _StreamMessageActionsModalState extends State<StreamMessageActionsModal> {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
if (widget.onReplyTap != null) {
|
||||
widget.onReplyTap!(widget.message);
|
||||
}
|
||||
widget.onReplyTap?.call(widget.message);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||
@@ -660,9 +658,7 @@ class _StreamMessageActionsModalState extends State<StreamMessageActionsModal> {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
if (widget.onThreadReplyTap != null) {
|
||||
widget.onThreadReplyTap!(widget.message);
|
||||
}
|
||||
widget.onThreadReplyTap?.call(widget.message);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||
|
||||
@@ -163,7 +163,7 @@ const _kDefaultMaxAttachmentSize = 20971520; // 20MB in Bytes
|
||||
class MessageInput extends StatefulWidget {
|
||||
/// Instantiate a new MessageInput
|
||||
const MessageInput({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.onMessageSent,
|
||||
this.preMessageSending,
|
||||
this.parentMessage,
|
||||
@@ -197,11 +197,10 @@ class MessageInput extends StatefulWidget {
|
||||
this.customOverlays = const [],
|
||||
this.mentionAllAppUsers = false,
|
||||
this.shouldKeepFocusAfterMessage,
|
||||
}) : assert(
|
||||
}) : assert(
|
||||
initialMessage == null || editMessage == null,
|
||||
"Can't provide both `initialMessage` and `editMessage`",
|
||||
),
|
||||
super(key: key);
|
||||
);
|
||||
|
||||
/// List of options for showing overlays
|
||||
final List<OverlayOptions> customOverlays;
|
||||
@@ -628,7 +627,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
color: _messageInputTheme.expandButtonColor,
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.all(0),
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints.tightFor(
|
||||
height: 24,
|
||||
width: 24,
|
||||
@@ -797,7 +796,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
child: IconButton(
|
||||
icon: StreamSvgIcon.closeSmall(),
|
||||
splashRadius: 24,
|
||||
padding: const EdgeInsets.all(0),
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints.tightFor(
|
||||
height: 24,
|
||||
width: 24,
|
||||
@@ -980,7 +979,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
return AnimatedContainer(
|
||||
duration: _openFilePickerSection
|
||||
? const Duration(milliseconds: 300)
|
||||
: const Duration(),
|
||||
: Duration.zero,
|
||||
curve: Curves.easeOut,
|
||||
height: _openFilePickerSection ? _kMinMediaPickerSize : 0,
|
||||
child: SingleChildScrollView(
|
||||
@@ -1034,7 +1033,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
padding: const EdgeInsets.all(0),
|
||||
padding: EdgeInsets.zero,
|
||||
icon: StreamSvgIcon.record(
|
||||
color: _getIconColor(3),
|
||||
),
|
||||
@@ -1436,9 +1435,9 @@ class MessageInputState extends State<MessageInput> {
|
||||
],
|
||||
);
|
||||
default:
|
||||
return Container(
|
||||
return const ColoredBox(
|
||||
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.actionButtonIdleColor),
|
||||
),
|
||||
padding: const EdgeInsets.all(0),
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints.tightFor(
|
||||
height: 24,
|
||||
width: 24,
|
||||
@@ -1482,7 +1481,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
? _messageInputTheme.actionButtonColor
|
||||
: _messageInputTheme.actionButtonIdleColor,
|
||||
),
|
||||
padding: const EdgeInsets.all(0),
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints.tightFor(
|
||||
height: 24,
|
||||
width: 24,
|
||||
@@ -1699,7 +1698,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: IconButton(
|
||||
onPressed: sendMessage,
|
||||
padding: const EdgeInsets.all(0),
|
||||
padding: EdgeInsets.zero,
|
||||
splashRadius: 24,
|
||||
constraints: const BoxConstraints.tightFor(
|
||||
height: 24,
|
||||
@@ -1926,7 +1925,6 @@ class MessageInputState extends State<MessageInput> {
|
||||
|
||||
class _PickerWidget extends StatefulWidget {
|
||||
const _PickerWidget({
|
||||
Key? key,
|
||||
required this.filePickerIndex,
|
||||
required this.containsFile,
|
||||
required this.selectedMedias,
|
||||
@@ -1934,7 +1932,7 @@ class _PickerWidget extends StatefulWidget {
|
||||
required this.onMediaSelected,
|
||||
required this.streamChatTheme,
|
||||
required this.mediaListViewController,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final int filePickerIndex;
|
||||
final bool containsFile;
|
||||
@@ -2002,7 +2000,7 @@ class _PickerWidgetState extends State<_PickerWidget> {
|
||||
onTap: () async {
|
||||
PhotoManager.openSetting();
|
||||
},
|
||||
child: Container(
|
||||
child: ColoredBox(
|
||||
color: widget.streamChatTheme.colorTheme.inputBg,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@@ -2040,10 +2038,7 @@ class _PickerWidgetState extends State<_PickerWidget> {
|
||||
}
|
||||
|
||||
class _CountdownButton extends StatelessWidget {
|
||||
const _CountdownButton({
|
||||
Key? key,
|
||||
required this.count,
|
||||
}) : super(key: key);
|
||||
const _CountdownButton({required this.count});
|
||||
|
||||
final int count;
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ typedef MessageListView = StreamMessageListView;
|
||||
class StreamMessageListView extends StatefulWidget {
|
||||
/// Instantiate a new StreamMessageListView.
|
||||
const StreamMessageListView({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.showScrollToBottom = true,
|
||||
this.scrollToBottomBuilder,
|
||||
this.messageBuilder,
|
||||
@@ -206,7 +206,7 @@ class StreamMessageListView extends StatefulWidget {
|
||||
this.paginationLoadingIndicatorBuilder,
|
||||
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.onDrag,
|
||||
this.spacingWidgetBuilder,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// [ScrollViewKeyboardDismissBehavior] the defines how this [PositionedList] will
|
||||
/// dismiss the keyboard automatically.
|
||||
@@ -751,7 +751,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
||||
StreamMessageListViewTheme.of(context).backgroundImage;
|
||||
|
||||
if (backgroundColor != null || backgroundImage != null) {
|
||||
return Container(
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
image: backgroundImage,
|
||||
@@ -889,7 +889,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
||||
initialIndex = 0;
|
||||
await streamChannel!.reloadChannel();
|
||||
|
||||
WidgetsBinding.instance?.addPostFrameCallback((_) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_scrollController!.jumpTo(index: 0);
|
||||
});
|
||||
} else {
|
||||
@@ -1039,9 +1039,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
||||
}
|
||||
},
|
||||
onMessageTap: (message) {
|
||||
if (widget.onMessageTap != null) {
|
||||
widget.onMessageTap!(message);
|
||||
}
|
||||
widget.onMessageTap?.call(message);
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
showPinButton: currentUserMember != null &&
|
||||
@@ -1066,9 +1064,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
||||
StreamSystemMessage(
|
||||
message: message,
|
||||
onMessageTap: (message) {
|
||||
if (widget.onSystemMessageTap != null) {
|
||||
widget.onSystemMessageTap!(message);
|
||||
}
|
||||
widget.onSystemMessageTap?.call(message);
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
);
|
||||
@@ -1230,9 +1226,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
||||
}
|
||||
},
|
||||
onMessageTap: (message) {
|
||||
if (widget.onMessageTap != null) {
|
||||
widget.onMessageTap!(message);
|
||||
}
|
||||
widget.onMessageTap?.call(message);
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
showPinButton: currentUserMember != null &&
|
||||
@@ -1287,8 +1281,8 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
||||
),
|
||||
duration: const Duration(seconds: 3),
|
||||
onEnd: () => initialMessageHighlightComplete = true,
|
||||
builder: (_, color, child) => Container(
|
||||
color: color,
|
||||
builder: (_, color, child) => ColoredBox(
|
||||
color: color!,
|
||||
child: child,
|
||||
),
|
||||
child: Padding(
|
||||
@@ -1341,7 +1335,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
||||
if (event.message?.parentId == widget.parentMessage?.id &&
|
||||
event.message!.user!.id ==
|
||||
streamChannel!.channel.client.state.currentUser!.id) {
|
||||
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_scrollController?.scrollTo(
|
||||
index: 0,
|
||||
duration: const Duration(seconds: 1),
|
||||
@@ -1389,11 +1383,10 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
||||
void _getOnThreadTap() {
|
||||
if (widget.onThreadTap != null) {
|
||||
_onThreadTap = (Message message) {
|
||||
final threadBuilder = widget.threadBuilder;
|
||||
widget.onThreadTap!(
|
||||
message,
|
||||
widget.threadBuilder != null
|
||||
? widget.threadBuilder!(context, message)
|
||||
: null,
|
||||
threadBuilder != null ? threadBuilder(context, message) : null,
|
||||
);
|
||||
};
|
||||
} else if (widget.threadBuilder != null) {
|
||||
@@ -1431,13 +1424,12 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
||||
|
||||
class _LoadingIndicator extends StatelessWidget {
|
||||
const _LoadingIndicator({
|
||||
Key? key,
|
||||
required this.streamTheme,
|
||||
required this.isThreadConversation,
|
||||
required this.direction,
|
||||
required this.streamChannel,
|
||||
this.indicatorBuilder,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final StreamChatThemeData streamTheme;
|
||||
final bool isThreadConversation;
|
||||
@@ -1454,7 +1446,7 @@ class _LoadingIndicator extends StatelessWidget {
|
||||
key: Key('LOADING-INDICATOR $direction'),
|
||||
stream: stream,
|
||||
initialData: false,
|
||||
errorBuilder: (context, error) => Container(
|
||||
errorBuilder: (context, error) => ColoredBox(
|
||||
color: streamTheme.colorTheme.accentError.withOpacity(0.2),
|
||||
child: Center(
|
||||
child: Text(context.translations.loadingMessagesError),
|
||||
|
||||
@@ -15,14 +15,14 @@ typedef MessageReactionsModal = StreamMessageReactionsModal;
|
||||
class StreamMessageReactionsModal extends StatelessWidget {
|
||||
/// Constructor for creating a [StreamMessageReactionsModal] reactions
|
||||
const StreamMessageReactionsModal({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.message,
|
||||
required this.messageWidget,
|
||||
required this.messageTheme,
|
||||
this.showReactions,
|
||||
this.reverse = false,
|
||||
this.onUserAvatarTap,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Widget that shows the message
|
||||
final Widget messageWidget;
|
||||
|
||||
@@ -16,11 +16,11 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
class MessageSearchItem extends StatelessWidget {
|
||||
/// Instantiate a new MessageSearchItem
|
||||
const MessageSearchItem({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.getMessageResponse,
|
||||
this.onTap,
|
||||
this.showOnlineStatus = true,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// [Message] displayed
|
||||
final GetMessageResponse getMessageResponse;
|
||||
|
||||
@@ -54,8 +54,9 @@ typedef EmptyMessageSearchBuilder = Widget Function(
|
||||
@Deprecated("Use 'StreamMessageSearchListView' instead")
|
||||
class MessageSearchListView extends StatefulWidget {
|
||||
/// Instantiate a new MessageSearchListView
|
||||
@Deprecated("Use 'StreamMessageSearchListView' instead")
|
||||
const MessageSearchListView({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.filters,
|
||||
this.messageQuery,
|
||||
this.sortOptions,
|
||||
@@ -72,7 +73,7 @@ class MessageSearchListView extends StatefulWidget {
|
||||
this.loadingBuilder,
|
||||
this.childBuilder,
|
||||
this.messageSearchListController,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Message String to search on
|
||||
final String? messageQuery;
|
||||
@@ -238,7 +239,7 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
||||
initialData: false,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
return Container(
|
||||
return ColoredBox(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentError
|
||||
|
||||
@@ -14,12 +14,12 @@ typedef MessageText = StreamMessageText;
|
||||
class StreamMessageText extends StatelessWidget {
|
||||
/// Constructor for creating a [StreamMessageText] widget
|
||||
const StreamMessageText({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.message,
|
||||
required this.messageTheme,
|
||||
this.onMentionTap,
|
||||
this.onLinkTap,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Message whose text is to be displayed
|
||||
final Message message;
|
||||
|
||||
@@ -52,7 +52,7 @@ typedef MessageWidget = StreamMessageWidget;
|
||||
class StreamMessageWidget extends StatefulWidget {
|
||||
/// Creates a new instance of the message widget.
|
||||
StreamMessageWidget({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.message,
|
||||
required this.messageTheme,
|
||||
this.reverse = false,
|
||||
@@ -105,7 +105,7 @@ class StreamMessageWidget extends StatefulWidget {
|
||||
this.customActions = const [],
|
||||
this.onAttachmentTap,
|
||||
this.usernameBuilder,
|
||||
}) : attachmentBuilders = {
|
||||
}) : attachmentBuilders = {
|
||||
'image': (context, message, attachments) {
|
||||
final border = RoundedRectangleBorder(
|
||||
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
||||
@@ -261,8 +261,7 @@ class StreamMessageWidget extends StatefulWidget {
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
}..addAll(customAttachmentBuilders ?? {}),
|
||||
super(key: key);
|
||||
}..addAll(customAttachmentBuilders ?? {});
|
||||
|
||||
/// Function called on mention tap
|
||||
final void Function(User)? onMentionTap;
|
||||
@@ -878,7 +877,7 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
|
||||
const Offstage();
|
||||
}
|
||||
|
||||
final children = <Widget>[];
|
||||
final children = <WidgetSpan>[];
|
||||
|
||||
final threadParticipants = widget.message.threadParticipants?.take(2);
|
||||
final showThreadParticipants = threadParticipants?.isNotEmpty == true;
|
||||
@@ -909,68 +908,72 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
|
||||
const usernameKey = Key('username');
|
||||
|
||||
children.addAll([
|
||||
if (showUsername) _buildUsername(usernameKey),
|
||||
if (showUsername) WidgetSpan(child: _buildUsername(usernameKey)),
|
||||
if (showTimeStamp)
|
||||
Text(
|
||||
Jiffy(widget.message.createdAt.toLocal()).jm,
|
||||
style: widget.messageTheme.createdAtStyle,
|
||||
WidgetSpan(
|
||||
child: Text(
|
||||
Jiffy(widget.message.createdAt.toLocal()).jm,
|
||||
style: widget.messageTheme.createdAtStyle,
|
||||
),
|
||||
),
|
||||
if (showSendingIndicator)
|
||||
WidgetSpan(
|
||||
child: _buildSendingIndicator(),
|
||||
),
|
||||
if (showSendingIndicator) _buildSendingIndicator(),
|
||||
]);
|
||||
|
||||
final showThreadTail = !(hasUrlAttachments || isGiphy || isOnlyEmoji) &&
|
||||
(showThreadReplyIndicator || showInChannel);
|
||||
|
||||
final threadIndicatorWidgets = <Widget>[
|
||||
final threadIndicatorWidgets = <WidgetSpan>[
|
||||
if (showThreadTail)
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: context.textScaleFactor *
|
||||
((widget.messageTheme.repliesStyle?.fontSize ?? 1) / 2),
|
||||
),
|
||||
child: CustomPaint(
|
||||
size: const Size(16, 32) * context.textScaleFactor,
|
||||
painter: _ThreadReplyPainter(
|
||||
context: context,
|
||||
color: widget.messageTheme.messageBorderColor,
|
||||
reverse: widget.reverse,
|
||||
WidgetSpan(
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: context.textScaleFactor *
|
||||
((widget.messageTheme.repliesStyle?.fontSize ?? 1) / 2),
|
||||
),
|
||||
child: CustomPaint(
|
||||
size: const Size(16, 32) * context.textScaleFactor,
|
||||
painter: _ThreadReplyPainter(
|
||||
context: context,
|
||||
color: widget.messageTheme.messageBorderColor,
|
||||
reverse: widget.reverse,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (showInChannel || showThreadReplyIndicator) ...[
|
||||
if (showThreadParticipants)
|
||||
SizedBox.fromSize(
|
||||
size: Size((threadParticipants!.length * 8.0) + 8, 16),
|
||||
child: _buildThreadParticipantsIndicator(threadParticipants),
|
||||
WidgetSpan(
|
||||
child: SizedBox.fromSize(
|
||||
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(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment:
|
||||
widget.reverse ? MainAxisAlignment.end : MainAxisAlignment.start,
|
||||
children: [
|
||||
if (showThreadTail && !widget.reverse) ...threadIndicatorWidgets,
|
||||
...children.map(
|
||||
(child) {
|
||||
Widget mappedChild = SizedBox(
|
||||
height: context.textScaleFactor * 14,
|
||||
child: child,
|
||||
);
|
||||
if (child.key == usernameKey) {
|
||||
mappedChild = Flexible(child: mappedChild);
|
||||
}
|
||||
return mappedChild;
|
||||
},
|
||||
),
|
||||
if (showThreadTail && widget.reverse)
|
||||
...threadIndicatorWidgets.reversed,
|
||||
].insertBetween(const SizedBox(width: 8)),
|
||||
if (widget.reverse) {
|
||||
children.addAll(threadIndicatorWidgets.reversed);
|
||||
} else {
|
||||
children.insertAll(0, threadIndicatorWidgets);
|
||||
}
|
||||
|
||||
return Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
...children,
|
||||
].insertBetween(const WidgetSpan(child: SizedBox(width: 8))),
|
||||
),
|
||||
maxLines: 1,
|
||||
textAlign: widget.reverse ? TextAlign.right : TextAlign.left,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1076,7 +1079,7 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
|
||||
showTimestamp: false,
|
||||
translateUserAvatar: false,
|
||||
showSendingIndicator: false,
|
||||
padding: const EdgeInsets.all(0),
|
||||
padding: EdgeInsets.zero,
|
||||
showReactionPickerIndicator: widget.showReactions &&
|
||||
(widget.message.status == MessageSendingStatus.sent) &&
|
||||
channel.ownCapabilities.contains(PermissionType.sendReaction),
|
||||
@@ -1107,6 +1110,13 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
|
||||
widget.onThreadTap != null,
|
||||
showFlagButton: widget.showFlagButton,
|
||||
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,
|
||||
translateUserAvatar: false,
|
||||
showSendingIndicator: false,
|
||||
padding: const EdgeInsets.all(0),
|
||||
padding: EdgeInsets.zero,
|
||||
showReactionPickerIndicator: widget.showReactions &&
|
||||
(widget.message.status == MessageSendingStatus.sent) &&
|
||||
channel.ownCapabilities.contains(PermissionType.sendReaction),
|
||||
@@ -1259,6 +1269,7 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
|
||||
);
|
||||
if (isMessageRead) {
|
||||
child = Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (memberCount > 2)
|
||||
Text(
|
||||
@@ -1396,11 +1407,9 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
|
||||
|
||||
class _ThreadParticipants extends StatelessWidget {
|
||||
const _ThreadParticipants({
|
||||
Key? key,
|
||||
required StreamChatThemeData streamChatTheme,
|
||||
required this.threadParticipants,
|
||||
}) : _streamChatTheme = streamChatTheme,
|
||||
super(key: key);
|
||||
}) : _streamChatTheme = streamChatTheme;
|
||||
|
||||
final StreamChatThemeData _streamChatTheme;
|
||||
final Iterable<User> threadParticipants;
|
||||
@@ -1423,7 +1432,7 @@ class _ThreadParticipants extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(1),
|
||||
child: StreamUserAvatar(
|
||||
user: user,
|
||||
constraints: BoxConstraints.loose(const Size.fromRadius(7)),
|
||||
constraints: BoxConstraints.tight(const Size.fromRadius(7)),
|
||||
showOnlineStatus: false,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -17,12 +17,12 @@ class StreamMultiOverlay extends StatelessWidget {
|
||||
/// [childAnchor] - the anchor relative to the child
|
||||
/// [child] - the child widget
|
||||
const StreamMultiOverlay({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.overlayOptions,
|
||||
required this.child,
|
||||
required this.overlayAnchor,
|
||||
required this.childAnchor,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// The list of overlay options
|
||||
final List<OverlayOptions> overlayOptions;
|
||||
|
||||
@@ -11,7 +11,7 @@ typedef OptionListTile = StreamOptionListTile;
|
||||
class StreamOptionListTile extends StatelessWidget {
|
||||
/// Constructor for creating [StreamOptionListTile]
|
||||
const StreamOptionListTile({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.title,
|
||||
this.leading,
|
||||
this.trailing,
|
||||
@@ -20,7 +20,7 @@ class StreamOptionListTile extends StatelessWidget {
|
||||
this.tileColor,
|
||||
this.separatorColor,
|
||||
this.titleTextStyle,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Title for tile
|
||||
final String title;
|
||||
|
||||
@@ -18,7 +18,7 @@ typedef QuotedMessageWidget = StreamQuotedMessageWidget;
|
||||
class StreamQuotedMessageWidget extends StatelessWidget {
|
||||
/// Creates a new instance of the widget.
|
||||
const StreamQuotedMessageWidget({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.message,
|
||||
required this.messageTheme,
|
||||
this.reverse = false,
|
||||
@@ -27,7 +27,7 @@ class StreamQuotedMessageWidget extends StatelessWidget {
|
||||
this.attachmentThumbnailBuilders,
|
||||
this.padding = const EdgeInsets.all(8),
|
||||
this.onTap,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// The message
|
||||
final Message message;
|
||||
@@ -251,12 +251,10 @@ class StreamQuotedMessageWidget extends StatelessWidget {
|
||||
|
||||
class _VideoAttachmentThumbnail extends StatefulWidget {
|
||||
const _VideoAttachmentThumbnail({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.attachment,
|
||||
this.size = const Size(32, 32),
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final Size size;
|
||||
final Attachment attachment;
|
||||
|
||||
@override
|
||||
@@ -285,8 +283,8 @@ class _VideoAttachmentThumbnailState extends State<_VideoAttachmentThumbnail> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => SizedBox(
|
||||
height: widget.size.height,
|
||||
width: widget.size.width,
|
||||
height: 32,
|
||||
width: 32,
|
||||
child: _controller.value.isInitialized
|
||||
? VideoPlayer(_controller)
|
||||
: const CircularProgressIndicator(),
|
||||
|
||||
@@ -14,7 +14,7 @@ typedef ReactionBubble = StreamReactionBubble;
|
||||
class StreamReactionBubble extends StatelessWidget {
|
||||
/// Constructor for creating a [StreamReactionBubble]
|
||||
const StreamReactionBubble({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.reactions,
|
||||
required this.borderColor,
|
||||
required this.backgroundColor,
|
||||
@@ -23,7 +23,7 @@ class StreamReactionBubble extends StatelessWidget {
|
||||
this.flipTail = false,
|
||||
this.highlightOwnReactions = true,
|
||||
this.tailCirclesSpacing = 0,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Reactions to show
|
||||
final List<Reaction> reactions;
|
||||
|
||||
@@ -19,9 +19,9 @@ typedef ReactionPicker = StreamReactionPicker;
|
||||
class StreamReactionPicker extends StatefulWidget {
|
||||
/// Constructor for creating a [StreamReactionPicker] widget
|
||||
const StreamReactionPicker({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.message,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Message to attach the reaction to
|
||||
final Message message;
|
||||
|
||||
@@ -11,11 +11,11 @@ typedef SendingIndicator = StreamSendingIndicator;
|
||||
class StreamSendingIndicator extends StatelessWidget {
|
||||
/// Constructor for creating a [StreamSendingIndicator] widget
|
||||
const StreamSendingIndicator({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.message,
|
||||
this.isMessageRead = false,
|
||||
this.size = 12,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
/// Message for sending indicator
|
||||
final Message message;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user