Merge remote-tracking branch 'origin/develop' into vale-linting
# Conflicts: # docusaurus/docs/Flutter/05-guides/05-push-notifications/adding_push_notifications_v2.mdx # packages/stream_chat_flutter/CHANGELOG.md
This commit is contained in:
@@ -1,3 +1,35 @@
|
||||
## Upcoming
|
||||
|
||||
🐞 Fixed
|
||||
|
||||
- Fixed streamWatchers. Before it was always new, now it is possible to follow the watchers of a channel.
|
||||
- Make `Message.i18n` field read-only.
|
||||
|
||||
🔄 Changed
|
||||
|
||||
- Cancelling a attachment upload now removes the attachment from the message.
|
||||
- Updated `dio` and other dependencies to resolvable versions.
|
||||
|
||||
✅ Added
|
||||
|
||||
- Added `presence` property to `Channel::watch` method.
|
||||
|
||||
## 5.3.0
|
||||
|
||||
🔄 Changed
|
||||
|
||||
- Updated `rate_limiter` dependency to `^1.0.0`
|
||||
|
||||
## 5.2.0
|
||||
|
||||
✅ Added
|
||||
|
||||
- Added `Huawei` and `Xiaomi` PushProviders.
|
||||
|
||||
🐞 Fixed
|
||||
|
||||
- Fixed initializing last synced date.
|
||||
|
||||
## 5.1.0
|
||||
|
||||
✅ Added
|
||||
|
||||
@@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 33
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
@@ -40,7 +40,7 @@ android {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.example.example"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 33
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
@@ -6,15 +6,16 @@
|
||||
additional functionality it is fine to subclass or reimplement
|
||||
FlutterApplication and put your custom class here. -->
|
||||
<application
|
||||
android:name="io.flutter.app.FlutterApplication"
|
||||
android:label="example"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="example">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:exported="true"
|
||||
android:hardwareAccelerated="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.50'
|
||||
ext.kotlin_version = '1.7.21'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
||||
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
|
||||
|
||||
@@ -6,6 +6,7 @@ version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: '>=2.17.0 <3.0.0'
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
dependencies:
|
||||
cupertino_icons: ^1.0.0
|
||||
|
||||
@@ -463,12 +463,19 @@ class Channel {
|
||||
|
||||
client.logger.info('Found ${attachments.length} attachments');
|
||||
|
||||
void updateAttachment(Attachment attachment) {
|
||||
void updateAttachment(Attachment attachment, {bool remove = false}) {
|
||||
final index = message!.attachments.indexWhere(
|
||||
(it) => it.id == attachment.id,
|
||||
);
|
||||
if (index != -1) {
|
||||
final newAttachments = [...message!.attachments]..[index] = attachment;
|
||||
// update or remove attachment from message.
|
||||
final List<Attachment> newAttachments;
|
||||
if (remove) {
|
||||
newAttachments = [...message!.attachments]..removeAt(index);
|
||||
} else {
|
||||
newAttachments = [...message!.attachments]..[index] = attachment;
|
||||
}
|
||||
|
||||
final updatedMessage = message!.copyWith(attachments: newAttachments);
|
||||
state?.updateMessage(updatedMessage);
|
||||
// updating original message for next iteration
|
||||
@@ -533,6 +540,14 @@ class Channel {
|
||||
);
|
||||
}
|
||||
}).catchError((e, stk) {
|
||||
if (e is StreamChatNetworkError && e.isRequestCancelledError) {
|
||||
client.logger.info('Attachment ${it.id} upload cancelled');
|
||||
|
||||
// remove attachment from message if cancelled.
|
||||
updateAttachment(it, remove: true);
|
||||
return;
|
||||
}
|
||||
|
||||
client.logger.severe('error uploading the attachment', e, stk);
|
||||
updateAttachment(
|
||||
it.copyWith(uploadState: UploadState.failed(error: e.toString())),
|
||||
@@ -1219,11 +1234,11 @@ class Channel {
|
||||
}
|
||||
|
||||
/// Loads the initial channel state and watches for changes.
|
||||
Future<ChannelState> watch() async {
|
||||
Future<ChannelState> watch({bool presence = false}) async {
|
||||
ChannelState response;
|
||||
|
||||
try {
|
||||
response = await query(watch: true);
|
||||
response = await query(watch: true, presence: presence);
|
||||
} catch (error, stackTrace) {
|
||||
if (!_initializedCompleter.isCompleted) {
|
||||
_initializedCompleter.completeError(error, stackTrace);
|
||||
@@ -1613,6 +1628,10 @@ class ChannelClientState {
|
||||
|
||||
_listenMemberUnbanned();
|
||||
|
||||
_listenUserStartWatching();
|
||||
|
||||
_listenUserStopWatching();
|
||||
|
||||
_startCleaningStaleTypingEvents();
|
||||
|
||||
_startCleaningStalePinnedMessages();
|
||||
@@ -1754,6 +1773,39 @@ class ChannelClientState {
|
||||
));
|
||||
}
|
||||
|
||||
void _listenUserStartWatching() {
|
||||
_subscriptions.add(
|
||||
_channel.on(EventType.userWatchingStart).listen((event) {
|
||||
final watcher = event.user;
|
||||
if (watcher != null) {
|
||||
final existingWatchers = channelState.watchers;
|
||||
updateChannelState(channelState.copyWith(
|
||||
watchers: [
|
||||
...?existingWatchers,
|
||||
watcher,
|
||||
],
|
||||
));
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
void _listenUserStopWatching() {
|
||||
_subscriptions.add(
|
||||
_channel.on(EventType.userWatchingStop).listen((event) {
|
||||
final watcher = event.user;
|
||||
if (watcher != null) {
|
||||
final existingWatchers = channelState.watchers;
|
||||
updateChannelState(channelState.copyWith(
|
||||
watchers: existingWatchers
|
||||
?.where((user) => user.id != watcher.id)
|
||||
.toList(growable: false),
|
||||
));
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
void _listenMemberUnbanned() {
|
||||
_subscriptions.add(_channel
|
||||
.on(EventType.userUnbanned)
|
||||
@@ -2083,7 +2135,7 @@ class ChannelClientState {
|
||||
channelStateStream.map((cs) => cs.watchers),
|
||||
_channel.client.state.usersStream,
|
||||
(watchers, users) => watchers!.map((e) => users[e.id] ?? e).toList(),
|
||||
);
|
||||
).distinct(const ListEquality().equals);
|
||||
|
||||
/// Channel member for the current user.
|
||||
Member? get currentUserMember => members.firstWhereOrNull(
|
||||
|
||||
@@ -453,6 +453,15 @@ class StreamChatClient {
|
||||
if (persistenceEnabled) {
|
||||
await sync(cids: cids, lastSyncAt: _lastSyncedAt);
|
||||
}
|
||||
} else {
|
||||
// channels are empty, assuming it's a fresh start
|
||||
// and making sure `lastSyncAt` is initialized
|
||||
if (persistenceEnabled) {
|
||||
final lastSyncAt = await _chatPersistenceClient?.getLastSyncAt();
|
||||
if (lastSyncAt == null) {
|
||||
await _chatPersistenceClient?.updateLastSyncAt(DateTime.now());
|
||||
}
|
||||
}
|
||||
}
|
||||
handleEvent(Event(
|
||||
type: EventType.connectionRecovered,
|
||||
|
||||
@@ -6,6 +6,12 @@ enum PushProvider {
|
||||
/// Send notifications using Google's Firebase Cloud Messaging
|
||||
firebase,
|
||||
|
||||
/// Send notifications using Huawei's Push Kit
|
||||
huawei,
|
||||
|
||||
/// Send notifications using Xiaomi's Mi Push Service
|
||||
xiaomi,
|
||||
|
||||
/// Send notifications using Apple's Push Notification service
|
||||
apn,
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class SortOption<T> {
|
||||
final int direction;
|
||||
|
||||
/// Sorting field Comparator required for offline sorting
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
final Comparator<T>? comparator;
|
||||
|
||||
/// Serialize model to json
|
||||
|
||||
@@ -74,6 +74,7 @@ class StreamChatNetworkError extends StreamChatError {
|
||||
ChatErrorCode errorCode, {
|
||||
int? statusCode,
|
||||
this.data,
|
||||
this.isRequestCancelledError = false,
|
||||
}) : code = errorCode.code,
|
||||
statusCode = statusCode ?? data?.statusCode,
|
||||
super(errorCode.message);
|
||||
@@ -84,6 +85,7 @@ class StreamChatNetworkError extends StreamChatError {
|
||||
required String message,
|
||||
this.statusCode,
|
||||
this.data,
|
||||
this.isRequestCancelledError = false,
|
||||
}) : super(message);
|
||||
|
||||
///
|
||||
@@ -96,10 +98,13 @@ class StreamChatNetworkError extends StreamChatError {
|
||||
}
|
||||
return StreamChatNetworkError.raw(
|
||||
code: errorResponse?.code ?? -1,
|
||||
message:
|
||||
errorResponse?.message ?? response?.statusMessage ?? error.message,
|
||||
message: errorResponse?.message ??
|
||||
response?.statusMessage ??
|
||||
error.message ??
|
||||
'',
|
||||
statusCode: errorResponse?.statusCode ?? response?.statusCode,
|
||||
data: errorResponse,
|
||||
isRequestCancelledError: error.type == DioErrorType.cancel,
|
||||
)..stackTrace = error.stackTrace;
|
||||
}
|
||||
|
||||
@@ -112,6 +117,9 @@ class StreamChatNetworkError extends StreamChatError {
|
||||
/// Response body. please refer to [ErrorResponse].
|
||||
final ErrorResponse? data;
|
||||
|
||||
/// True, in case the error is due to a cancelled network request.
|
||||
final bool isRequestCancelledError;
|
||||
|
||||
StackTrace? _stackTrace;
|
||||
|
||||
///
|
||||
|
||||
@@ -87,8 +87,8 @@ class LoggingInterceptor extends Interceptor {
|
||||
requestHeaders['contentType'] = options.contentType?.toString();
|
||||
requestHeaders['responseType'] = options.responseType.toString();
|
||||
requestHeaders['followRedirects'] = options.followRedirects;
|
||||
requestHeaders['connectTimeout'] = options.connectTimeout;
|
||||
requestHeaders['receiveTimeout'] = options.receiveTimeout;
|
||||
requestHeaders['connectTimeout'] = options.connectTimeout?.toString();
|
||||
requestHeaders['receiveTimeout'] = options.receiveTimeout?.toString();
|
||||
_printMapAsTable(_logPrintRequest, requestHeaders, header: 'Headers');
|
||||
_printMapAsTable(_logPrintRequest, options.extra, header: 'Extras');
|
||||
}
|
||||
@@ -101,7 +101,8 @@ class LoggingInterceptor extends Interceptor {
|
||||
options.data as Map?,
|
||||
header: 'Body',
|
||||
);
|
||||
} else if (data is FormData) {
|
||||
}
|
||||
if (data is FormData) {
|
||||
final formDataMap = <String, dynamic>{}
|
||||
..addEntries(data.fields)
|
||||
..addEntries(data.files);
|
||||
@@ -121,7 +122,7 @@ class LoggingInterceptor extends Interceptor {
|
||||
@override
|
||||
void onError(DioError err, ErrorInterceptorHandler handler) {
|
||||
if (error) {
|
||||
if (err.type == DioErrorType.response) {
|
||||
if (err.type == DioErrorType.badResponse) {
|
||||
final uri = err.response?.requestOptions.uri;
|
||||
_printBoxed(
|
||||
_logPrintError,
|
||||
@@ -162,7 +163,7 @@ class LoggingInterceptor extends Interceptor {
|
||||
_logPrintResponse('║');
|
||||
_printResponse(_logPrintResponse, response);
|
||||
_logPrintResponse('║');
|
||||
_printLine(_logPrintResponse, '╚');
|
||||
_logPrintResponse('╚');
|
||||
}
|
||||
super.onResponse(response, handler);
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ class StreamHttpClient {
|
||||
httpClient = dio ?? Dio() {
|
||||
httpClient
|
||||
..options.baseUrl = _options.baseUrl
|
||||
..options.receiveTimeout = _options.receiveTimeout.inMilliseconds
|
||||
..options.connectTimeout = _options.connectTimeout.inMilliseconds
|
||||
..options.receiveTimeout = _options.receiveTimeout
|
||||
..options.connectTimeout = _options.connectTimeout
|
||||
..options.queryParameters = {
|
||||
'api_key': apiKey,
|
||||
..._options.queryParameters,
|
||||
|
||||
@@ -135,7 +135,6 @@ class Attachment extends Equatable {
|
||||
late final UploadState uploadState;
|
||||
|
||||
/// Map of custom channel extraData
|
||||
@JsonKey(includeIfNull: false)
|
||||
final Map<String, Object?> extraData;
|
||||
|
||||
/// The attachment ID.
|
||||
@@ -146,13 +145,13 @@ class Attachment extends Equatable {
|
||||
/// Shortcut for file size.
|
||||
///
|
||||
/// {@macro fileSize}
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
int? get fileSize => extraData['file_size'] as int?;
|
||||
|
||||
/// Shortcut for file mimeType.
|
||||
///
|
||||
/// {@macro mimeType}
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
String? get mimeType => extraData['mime_type'] as String?;
|
||||
|
||||
/// Known top level fields.
|
||||
|
||||
@@ -52,7 +52,7 @@ class AttachmentFile {
|
||||
|
||||
/// Byte data for this file. Particularly useful if you want to manipulate
|
||||
/// its data or easily upload to somewhere else.
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
final Uint8List? bytes;
|
||||
|
||||
/// The file size in bytes.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'attachment_file.dart';
|
||||
|
||||
@@ -43,10 +43,10 @@ mixin _$UploadState {
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult Function()? preparing,
|
||||
TResult Function(int uploaded, int total)? inProgress,
|
||||
TResult Function()? success,
|
||||
TResult Function(String error)? failed,
|
||||
TResult? Function()? preparing,
|
||||
TResult? Function(int uploaded, int total)? inProgress,
|
||||
TResult? Function()? success,
|
||||
TResult? Function(String error)? failed,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
@@ -68,10 +68,10 @@ mixin _$UploadState {
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult Function(Preparing value)? preparing,
|
||||
TResult Function(InProgress value)? inProgress,
|
||||
TResult Function(Success value)? success,
|
||||
TResult Function(Failed value)? failed,
|
||||
TResult? Function(Preparing value)? preparing,
|
||||
TResult? Function(InProgress value)? inProgress,
|
||||
TResult? Function(Success value)? success,
|
||||
TResult? Function(Failed value)? failed,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
@@ -90,16 +90,18 @@ mixin _$UploadState {
|
||||
abstract class $UploadStateCopyWith<$Res> {
|
||||
factory $UploadStateCopyWith(
|
||||
UploadState value, $Res Function(UploadState) then) =
|
||||
_$UploadStateCopyWithImpl<$Res>;
|
||||
_$UploadStateCopyWithImpl<$Res, UploadState>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$UploadStateCopyWithImpl<$Res> implements $UploadStateCopyWith<$Res> {
|
||||
class _$UploadStateCopyWithImpl<$Res, $Val extends UploadState>
|
||||
implements $UploadStateCopyWith<$Res> {
|
||||
_$UploadStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
final UploadState _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function(UploadState) _then;
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -110,14 +112,12 @@ abstract class _$$PreparingCopyWith<$Res> {
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$PreparingCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
||||
class __$$PreparingCopyWithImpl<$Res>
|
||||
extends _$UploadStateCopyWithImpl<$Res, _$Preparing>
|
||||
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;
|
||||
: super(_value, _then);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -162,10 +162,10 @@ class _$Preparing extends Preparing {
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult Function()? preparing,
|
||||
TResult Function(int uploaded, int total)? inProgress,
|
||||
TResult Function()? success,
|
||||
TResult Function(String error)? failed,
|
||||
TResult? Function()? preparing,
|
||||
TResult? Function(int uploaded, int total)? inProgress,
|
||||
TResult? Function()? success,
|
||||
TResult? Function(String error)? failed,
|
||||
}) {
|
||||
return preparing?.call();
|
||||
}
|
||||
@@ -199,10 +199,10 @@ class _$Preparing extends Preparing {
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult Function(Preparing value)? preparing,
|
||||
TResult Function(InProgress value)? inProgress,
|
||||
TResult Function(Success value)? success,
|
||||
TResult Function(Failed value)? failed,
|
||||
TResult? Function(Preparing value)? preparing,
|
||||
TResult? Function(InProgress value)? inProgress,
|
||||
TResult? Function(Success value)? success,
|
||||
TResult? Function(Failed value)? failed,
|
||||
}) {
|
||||
return preparing?.call(this);
|
||||
}
|
||||
@@ -242,30 +242,30 @@ abstract class _$$InProgressCopyWith<$Res> {
|
||||
factory _$$InProgressCopyWith(
|
||||
_$InProgress value, $Res Function(_$InProgress) then) =
|
||||
__$$InProgressCopyWithImpl<$Res>;
|
||||
@useResult
|
||||
$Res call({int uploaded, int total});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
||||
class __$$InProgressCopyWithImpl<$Res>
|
||||
extends _$UploadStateCopyWithImpl<$Res, _$InProgress>
|
||||
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;
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? uploaded = freezed,
|
||||
Object? total = freezed,
|
||||
Object? uploaded = null,
|
||||
Object? total = null,
|
||||
}) {
|
||||
return _then(_$InProgress(
|
||||
uploaded: uploaded == freezed
|
||||
uploaded: null == uploaded
|
||||
? _value.uploaded
|
||||
: uploaded // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
total: total == freezed
|
||||
total: null == total
|
||||
? _value.total
|
||||
: total // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
@@ -302,19 +302,18 @@ class _$InProgress extends InProgress {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$InProgress &&
|
||||
const DeepCollectionEquality().equals(other.uploaded, uploaded) &&
|
||||
const DeepCollectionEquality().equals(other.total, total));
|
||||
(identical(other.uploaded, uploaded) ||
|
||||
other.uploaded == uploaded) &&
|
||||
(identical(other.total, total) || other.total == total));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(uploaded),
|
||||
const DeepCollectionEquality().hash(total));
|
||||
int get hashCode => Object.hash(runtimeType, uploaded, total);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$InProgressCopyWith<_$InProgress> get copyWith =>
|
||||
__$$InProgressCopyWithImpl<_$InProgress>(this, _$identity);
|
||||
|
||||
@@ -332,10 +331,10 @@ class _$InProgress extends InProgress {
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult Function()? preparing,
|
||||
TResult Function(int uploaded, int total)? inProgress,
|
||||
TResult Function()? success,
|
||||
TResult Function(String error)? failed,
|
||||
TResult? Function()? preparing,
|
||||
TResult? Function(int uploaded, int total)? inProgress,
|
||||
TResult? Function()? success,
|
||||
TResult? Function(String error)? failed,
|
||||
}) {
|
||||
return inProgress?.call(uploaded, total);
|
||||
}
|
||||
@@ -369,10 +368,10 @@ class _$InProgress extends InProgress {
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult Function(Preparing value)? preparing,
|
||||
TResult Function(InProgress value)? inProgress,
|
||||
TResult Function(Success value)? success,
|
||||
TResult Function(Failed value)? failed,
|
||||
TResult? Function(Preparing value)? preparing,
|
||||
TResult? Function(InProgress value)? inProgress,
|
||||
TResult? Function(Success value)? success,
|
||||
TResult? Function(Failed value)? failed,
|
||||
}) {
|
||||
return inProgress?.call(this);
|
||||
}
|
||||
@@ -422,13 +421,11 @@ abstract class _$$SuccessCopyWith<$Res> {
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$SuccessCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
||||
class __$$SuccessCopyWithImpl<$Res>
|
||||
extends _$UploadStateCopyWithImpl<$Res, _$Success>
|
||||
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;
|
||||
: super(_value, _then);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -473,10 +470,10 @@ class _$Success extends Success {
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult Function()? preparing,
|
||||
TResult Function(int uploaded, int total)? inProgress,
|
||||
TResult Function()? success,
|
||||
TResult Function(String error)? failed,
|
||||
TResult? Function()? preparing,
|
||||
TResult? Function(int uploaded, int total)? inProgress,
|
||||
TResult? Function()? success,
|
||||
TResult? Function(String error)? failed,
|
||||
}) {
|
||||
return success?.call();
|
||||
}
|
||||
@@ -510,10 +507,10 @@ class _$Success extends Success {
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult Function(Preparing value)? preparing,
|
||||
TResult Function(InProgress value)? inProgress,
|
||||
TResult Function(Success value)? success,
|
||||
TResult Function(Failed value)? failed,
|
||||
TResult? Function(Preparing value)? preparing,
|
||||
TResult? Function(InProgress value)? inProgress,
|
||||
TResult? Function(Success value)? success,
|
||||
TResult? Function(Failed value)? failed,
|
||||
}) {
|
||||
return success?.call(this);
|
||||
}
|
||||
@@ -552,24 +549,24 @@ abstract class Success extends UploadState {
|
||||
abstract class _$$FailedCopyWith<$Res> {
|
||||
factory _$$FailedCopyWith(_$Failed value, $Res Function(_$Failed) then) =
|
||||
__$$FailedCopyWithImpl<$Res>;
|
||||
@useResult
|
||||
$Res call({String error});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
||||
class __$$FailedCopyWithImpl<$Res>
|
||||
extends _$UploadStateCopyWithImpl<$Res, _$Failed>
|
||||
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;
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? error = freezed,
|
||||
Object? error = null,
|
||||
}) {
|
||||
return _then(_$Failed(
|
||||
error: error == freezed
|
||||
error: null == error
|
||||
? _value.error
|
||||
: error // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
@@ -603,16 +600,16 @@ class _$Failed extends Failed {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$Failed &&
|
||||
const DeepCollectionEquality().equals(other.error, error));
|
||||
(identical(other.error, error) || other.error == error));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(error));
|
||||
int get hashCode => Object.hash(runtimeType, error);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$FailedCopyWith<_$Failed> get copyWith =>
|
||||
__$$FailedCopyWithImpl<_$Failed>(this, _$identity);
|
||||
|
||||
@@ -630,10 +627,10 @@ class _$Failed extends Failed {
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult Function()? preparing,
|
||||
TResult Function(int uploaded, int total)? inProgress,
|
||||
TResult Function()? success,
|
||||
TResult Function(String error)? failed,
|
||||
TResult? Function()? preparing,
|
||||
TResult? Function(int uploaded, int total)? inProgress,
|
||||
TResult? Function()? success,
|
||||
TResult? Function(String error)? failed,
|
||||
}) {
|
||||
return failed?.call(error);
|
||||
}
|
||||
@@ -667,10 +664,10 @@ class _$Failed extends Failed {
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult Function(Preparing value)? preparing,
|
||||
TResult Function(InProgress value)? inProgress,
|
||||
TResult Function(Success value)? success,
|
||||
TResult Function(Failed value)? failed,
|
||||
TResult? Function(Preparing value)? preparing,
|
||||
TResult? Function(InProgress value)? inProgress,
|
||||
TResult? Function(Success value)? success,
|
||||
TResult? Function(Failed value)? failed,
|
||||
}) {
|
||||
return failed?.call(this);
|
||||
}
|
||||
|
||||
@@ -62,19 +62,19 @@ class ChannelModel {
|
||||
final String type;
|
||||
|
||||
/// The cid of this channel
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final String cid;
|
||||
|
||||
/// List of user permissions on this channel
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final List<String>? ownCapabilities;
|
||||
|
||||
/// The channel configuration data
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final ChannelConfig config;
|
||||
|
||||
/// The user that created this channel
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final User? createdBy;
|
||||
|
||||
/// True if this channel is frozen
|
||||
@@ -82,23 +82,23 @@ class ChannelModel {
|
||||
final bool frozen;
|
||||
|
||||
/// The date of the last message
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final DateTime? lastMessageAt;
|
||||
|
||||
/// The date of channel creation
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final DateTime createdAt;
|
||||
|
||||
/// The date of the last channel update
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final DateTime updatedAt;
|
||||
|
||||
/// The date of channel deletion
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final DateTime? deletedAt;
|
||||
|
||||
/// The count of this channel members
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final int memberCount;
|
||||
|
||||
/// The number of seconds in a cooldown
|
||||
@@ -106,15 +106,15 @@ class ChannelModel {
|
||||
final int cooldown;
|
||||
|
||||
/// True if the channel is disabled
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
bool? get disabled => extraData['disabled'] as bool?;
|
||||
|
||||
/// True if the channel is hidden
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
bool? get hidden => extraData['hidden'] as bool?;
|
||||
|
||||
/// The date of the last time channel got truncated
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
DateTime? get truncatedAt {
|
||||
final truncatedAt = extraData['truncated_at'] as String?;
|
||||
if (truncatedAt == null) return null;
|
||||
@@ -122,11 +122,10 @@ class ChannelModel {
|
||||
}
|
||||
|
||||
/// Map of custom channel extraData
|
||||
@JsonKey(includeIfNull: false)
|
||||
final Map<String, Object?> extraData;
|
||||
|
||||
/// The team the channel belongs to
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final String? team;
|
||||
|
||||
/// Known top level fields.
|
||||
|
||||
@@ -38,30 +38,11 @@ ChannelModel _$ChannelModelFromJson(Map<String, dynamic> json) => ChannelModel(
|
||||
cooldown: json['cooldown'] as int? ?? 0,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ChannelModelToJson(ChannelModel instance) {
|
||||
final val = <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'type': instance.type,
|
||||
};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('cid', readonly(instance.cid));
|
||||
writeNotNull('own_capabilities', readonly(instance.ownCapabilities));
|
||||
writeNotNull('config', readonly(instance.config));
|
||||
writeNotNull('created_by', readonly(instance.createdBy));
|
||||
val['frozen'] = instance.frozen;
|
||||
writeNotNull('last_message_at', readonly(instance.lastMessageAt));
|
||||
writeNotNull('created_at', readonly(instance.createdAt));
|
||||
writeNotNull('updated_at', readonly(instance.updatedAt));
|
||||
writeNotNull('deleted_at', readonly(instance.deletedAt));
|
||||
writeNotNull('member_count', readonly(instance.memberCount));
|
||||
val['cooldown'] = instance.cooldown;
|
||||
val['extra_data'] = instance.extraData;
|
||||
writeNotNull('team', readonly(instance.team));
|
||||
return val;
|
||||
}
|
||||
Map<String, dynamic> _$ChannelModelToJson(ChannelModel instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'type': instance.type,
|
||||
'frozen': instance.frozen,
|
||||
'cooldown': instance.cooldown,
|
||||
'extra_data': instance.extraData,
|
||||
};
|
||||
|
||||
@@ -95,14 +95,11 @@ class Message extends Equatable {
|
||||
final String? text;
|
||||
|
||||
/// The status of a sending message.
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
final MessageSendingStatus status;
|
||||
|
||||
/// The message type.
|
||||
@JsonKey(
|
||||
includeIfNull: false,
|
||||
toJson: Serializer.readOnly,
|
||||
)
|
||||
@JsonKey(includeToJson: false)
|
||||
final String type;
|
||||
|
||||
/// The list of attachments, either provided by the user or generated from a
|
||||
@@ -115,26 +112,26 @@ class Message extends Equatable {
|
||||
final List<User> mentionedUsers;
|
||||
|
||||
/// A map describing the count of number of every reaction.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final Map<String, int>? reactionCounts;
|
||||
|
||||
/// A map describing the count of score of every reaction.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final Map<String, int>? reactionScores;
|
||||
|
||||
/// The latest reactions to the message created by any user.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final List<Reaction>? latestReactions;
|
||||
|
||||
/// The reactions added to the message by the current user.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final List<Reaction>? ownReactions;
|
||||
|
||||
/// The ID of the parent message, if the message is a thread reply.
|
||||
final String? parentId;
|
||||
|
||||
/// A quoted reply message.
|
||||
@JsonKey(toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final Message? quotedMessage;
|
||||
|
||||
final String? _quotedMessageId;
|
||||
@@ -143,11 +140,11 @@ class Message extends Equatable {
|
||||
String? get quotedMessageId => _quotedMessageId ?? quotedMessage?.id;
|
||||
|
||||
/// Reserved field indicating the number of replies for this message.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final int? replyCount;
|
||||
|
||||
/// Reserved field indicating the thread participants for this message.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final List<User>? threadParticipants;
|
||||
|
||||
/// Check if this message needs to show in the channel.
|
||||
@@ -157,41 +154,38 @@ class Message extends Equatable {
|
||||
final bool silent;
|
||||
|
||||
/// If true the message is shadowed.
|
||||
@JsonKey(
|
||||
includeIfNull: false,
|
||||
toJson: Serializer.readOnly,
|
||||
)
|
||||
@JsonKey(includeToJson: false)
|
||||
final bool shadowed;
|
||||
|
||||
/// A used command name.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final String? command;
|
||||
|
||||
final DateTime? _createdAt;
|
||||
|
||||
/// Reserved field indicating when the message was deleted.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final DateTime? deletedAt;
|
||||
|
||||
/// Reserved field indicating when the message was created.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
DateTime get createdAt => _createdAt ?? DateTime.now();
|
||||
|
||||
final DateTime? _updatedAt;
|
||||
|
||||
/// Reserved field indicating when the message was updated last time.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
DateTime get updatedAt => _updatedAt ?? DateTime.now();
|
||||
|
||||
/// User who sent the message.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final User? user;
|
||||
|
||||
/// If true the message is pinned.
|
||||
final bool pinned;
|
||||
|
||||
/// Reserved field indicating when the message was pinned.
|
||||
@JsonKey(toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final DateTime? pinnedAt;
|
||||
|
||||
/// Reserved field indicating when the message will expire.
|
||||
@@ -200,11 +194,10 @@ class Message extends Equatable {
|
||||
final DateTime? pinExpires;
|
||||
|
||||
/// Reserved field indicating who pinned the message.
|
||||
@JsonKey(toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final User? pinnedBy;
|
||||
|
||||
/// Message custom extraData.
|
||||
@JsonKey(includeIfNull: false)
|
||||
final Map<String, Object?> extraData;
|
||||
|
||||
/// True if the message is a system info.
|
||||
@@ -217,7 +210,7 @@ class Message extends Equatable {
|
||||
bool get isEphemeral => type == 'ephemeral';
|
||||
|
||||
/// A Map of translations.
|
||||
@JsonKey(includeIfNull: false)
|
||||
@JsonKey(includeToJson: false)
|
||||
final Map<String, String>? i18n;
|
||||
|
||||
/// Known top level fields.
|
||||
|
||||
@@ -71,43 +71,16 @@ Message _$MessageFromJson(Map<String, dynamic> json) => Message(
|
||||
),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$MessageToJson(Message instance) {
|
||||
final val = <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'text': instance.text,
|
||||
};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('type', readonly(instance.type));
|
||||
val['attachments'] = instance.attachments.map((e) => e.toJson()).toList();
|
||||
val['mentioned_users'] = User.toIds(instance.mentionedUsers);
|
||||
writeNotNull('reaction_counts', readonly(instance.reactionCounts));
|
||||
writeNotNull('reaction_scores', readonly(instance.reactionScores));
|
||||
writeNotNull('latest_reactions', readonly(instance.latestReactions));
|
||||
writeNotNull('own_reactions', readonly(instance.ownReactions));
|
||||
val['parent_id'] = instance.parentId;
|
||||
val['quoted_message'] = readonly(instance.quotedMessage);
|
||||
val['quoted_message_id'] = instance.quotedMessageId;
|
||||
writeNotNull('reply_count', readonly(instance.replyCount));
|
||||
writeNotNull('thread_participants', readonly(instance.threadParticipants));
|
||||
val['show_in_channel'] = instance.showInChannel;
|
||||
val['silent'] = instance.silent;
|
||||
writeNotNull('shadowed', readonly(instance.shadowed));
|
||||
writeNotNull('command', readonly(instance.command));
|
||||
writeNotNull('deleted_at', readonly(instance.deletedAt));
|
||||
writeNotNull('created_at', readonly(instance.createdAt));
|
||||
writeNotNull('updated_at', readonly(instance.updatedAt));
|
||||
writeNotNull('user', readonly(instance.user));
|
||||
val['pinned'] = instance.pinned;
|
||||
val['pinned_at'] = readonly(instance.pinnedAt);
|
||||
val['pin_expires'] = instance.pinExpires?.toIso8601String();
|
||||
val['pinned_by'] = readonly(instance.pinnedBy);
|
||||
val['extra_data'] = instance.extraData;
|
||||
writeNotNull('i18n', instance.i18n);
|
||||
return val;
|
||||
}
|
||||
Map<String, dynamic> _$MessageToJson(Message instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'text': instance.text,
|
||||
'attachments': instance.attachments.map((e) => e.toJson()).toList(),
|
||||
'mentioned_users': User.toIds(instance.mentionedUsers),
|
||||
'parent_id': instance.parentId,
|
||||
'quoted_message_id': instance.quotedMessageId,
|
||||
'show_in_channel': instance.showInChannel,
|
||||
'silent': instance.silent,
|
||||
'pinned': instance.pinned,
|
||||
'pin_expires': instance.pinExpires?.toIso8601String(),
|
||||
'extra_data': instance.extraData,
|
||||
};
|
||||
|
||||
@@ -33,22 +33,21 @@ class Reaction {
|
||||
final String type;
|
||||
|
||||
/// The date of the reaction
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final DateTime createdAt;
|
||||
|
||||
/// The user that sent the reaction
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final User? user;
|
||||
|
||||
/// The score of the reaction (ie. number of reactions sent)
|
||||
final int score;
|
||||
|
||||
/// The userId that sent the reaction
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final String? userId;
|
||||
|
||||
/// Reaction custom extraData
|
||||
@JsonKey(includeIfNull: false)
|
||||
final Map<String, Object?> extraData;
|
||||
|
||||
/// Map of custom user extraData
|
||||
|
||||
@@ -20,22 +20,9 @@ Reaction _$ReactionFromJson(Map<String, dynamic> json) => Reaction(
|
||||
extraData: json['extra_data'] as Map<String, dynamic>? ?? const {},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ReactionToJson(Reaction instance) {
|
||||
final val = <String, dynamic>{
|
||||
'message_id': instance.messageId,
|
||||
'type': instance.type,
|
||||
};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('created_at', readonly(instance.createdAt));
|
||||
writeNotNull('user', readonly(instance.user));
|
||||
val['score'] = instance.score;
|
||||
writeNotNull('user_id', readonly(instance.userId));
|
||||
val['extra_data'] = instance.extraData;
|
||||
return val;
|
||||
}
|
||||
Map<String, dynamic> _$ReactionToJson(Reaction instance) => <String, dynamic>{
|
||||
'message_id': instance.messageId,
|
||||
'type': instance.type,
|
||||
'score': instance.score,
|
||||
'extra_data': instance.extraData,
|
||||
};
|
||||
|
||||
@@ -79,7 +79,7 @@ class User extends Equatable {
|
||||
/// Shortcut for user name.
|
||||
///
|
||||
/// {@macro name}
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
String get name {
|
||||
if (extraData.containsKey('name') && extraData['name'] != null) {
|
||||
final name = extraData['name']! as String;
|
||||
@@ -91,48 +91,39 @@ class User extends Equatable {
|
||||
/// Shortcut for user image.
|
||||
///
|
||||
/// {@macro image}
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
String? get image => extraData['image'] as String?;
|
||||
|
||||
/// User role.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final String? role;
|
||||
|
||||
/// User teams
|
||||
@JsonKey(
|
||||
includeIfNull: false,
|
||||
toJson: Serializer.readOnly,
|
||||
)
|
||||
@JsonKey(includeToJson: false)
|
||||
final List<String> teams;
|
||||
|
||||
/// Date of user creation.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final DateTime createdAt;
|
||||
|
||||
/// Date of last user update.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final DateTime updatedAt;
|
||||
|
||||
/// Date of last user connection.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final DateTime? lastActive;
|
||||
|
||||
/// True if user is online.
|
||||
@JsonKey(
|
||||
includeIfNull: false,
|
||||
toJson: Serializer.readOnly,
|
||||
)
|
||||
@JsonKey(includeToJson: false)
|
||||
final bool online;
|
||||
|
||||
/// True if user is banned from the chat.
|
||||
@JsonKey(
|
||||
includeIfNull: false,
|
||||
toJson: Serializer.readOnly,
|
||||
)
|
||||
@JsonKey(includeToJson: false)
|
||||
final bool banned;
|
||||
|
||||
/// The date at which the ban will expire.
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||
@JsonKey(includeToJson: false)
|
||||
final DateTime? banExpires;
|
||||
|
||||
/// The language this user prefers.
|
||||
@@ -140,7 +131,6 @@ class User extends Equatable {
|
||||
final String? language;
|
||||
|
||||
/// Map of custom user extraData.
|
||||
@JsonKey(includeIfNull: false)
|
||||
final Map<String, Object?> extraData;
|
||||
|
||||
/// List of users to list of userIds.
|
||||
|
||||
@@ -41,14 +41,6 @@ Map<String, dynamic> _$UserToJson(User instance) {
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('role', readonly(instance.role));
|
||||
writeNotNull('teams', readonly(instance.teams));
|
||||
writeNotNull('created_at', readonly(instance.createdAt));
|
||||
writeNotNull('updated_at', readonly(instance.updatedAt));
|
||||
writeNotNull('last_active', readonly(instance.lastActive));
|
||||
writeNotNull('online', readonly(instance.online));
|
||||
writeNotNull('banned', readonly(instance.banned));
|
||||
writeNotNull('ban_expires', readonly(instance.banExpires));
|
||||
writeNotNull('language', instance.language);
|
||||
val['extra_data'] = instance.extraData;
|
||||
return val;
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
/// Used to avoid to serialize properties to json
|
||||
// ignore: prefer_void_to_null
|
||||
Null readonly(_) => null;
|
||||
|
||||
/// Helper class for serialization to and from json
|
||||
class Serializer {
|
||||
/// Used to avoid to serialize properties to json
|
||||
static const Function readOnly = readonly;
|
||||
|
||||
/// Takes unknown json keys and puts them in the `extra_data` key
|
||||
static Map<String, dynamic> moveToExtraDataFromRoot(
|
||||
Map<String, dynamic> json,
|
||||
|
||||
@@ -36,6 +36,12 @@ class EventType {
|
||||
/// Event sent when updating a message
|
||||
static const String messageUpdated = 'message.updated';
|
||||
|
||||
/// Event sent when a user starts watching a channel
|
||||
static const String userWatchingStart = 'user.watching.start';
|
||||
|
||||
/// Event sent when a user stops watching a channel
|
||||
static const String userWatchingStop = 'user.watching.stop';
|
||||
|
||||
/// Event sent when reading a message
|
||||
static const String messageRead = 'message.read';
|
||||
|
||||
|
||||
@@ -10,41 +10,13 @@ export 'package:logging/logging.dart' show Logger, Level, LogRecord;
|
||||
export 'package:rate_limiter/rate_limiter.dart';
|
||||
export 'package:uuid/uuid.dart';
|
||||
|
||||
export './src/core/api/attachment_file_uploader.dart';
|
||||
export './src/core/api/requests.dart';
|
||||
export './src/core/api/requests.dart';
|
||||
export './src/core/api/responses.dart';
|
||||
export './src/core/api/stream_chat_api.dart' show PushProvider;
|
||||
export './src/core/error/error.dart';
|
||||
export './src/core/models/action.dart';
|
||||
export './src/core/models/attachment.dart';
|
||||
export './src/core/models/attachment_file.dart';
|
||||
export './src/core/models/channel_config.dart';
|
||||
export './src/core/models/channel_model.dart';
|
||||
export './src/core/models/channel_state.dart';
|
||||
export './src/core/models/command.dart';
|
||||
export './src/core/models/device.dart';
|
||||
export './src/core/models/event.dart';
|
||||
export './src/core/models/filter.dart' show Filter;
|
||||
export './src/core/models/member.dart';
|
||||
export './src/core/models/message.dart';
|
||||
export './src/core/models/mute.dart';
|
||||
export './src/core/models/own_user.dart';
|
||||
export './src/core/models/reaction.dart';
|
||||
export './src/core/models/read.dart';
|
||||
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/permission_type.dart';
|
||||
export './src/ws/connection_status.dart';
|
||||
export 'src/client/channel.dart';
|
||||
export 'src/client/client.dart';
|
||||
export 'src/client/key_stroke_handler.dart';
|
||||
export 'src/core/api/attachment_file_uploader.dart' show AttachmentFileUploader;
|
||||
export 'src/core/api/requests.dart';
|
||||
export 'src/core/api/attachment_file_uploader.dart';
|
||||
export 'src/core/api/requests.dart';
|
||||
export 'src/core/api/responses.dart';
|
||||
export 'src/core/api/stream_chat_api.dart' show PushProvider;
|
||||
export 'src/core/api/stream_chat_api.dart';
|
||||
export 'src/core/error/error.dart';
|
||||
export 'src/core/models/action.dart';
|
||||
@@ -68,4 +40,5 @@ export 'src/core/platform_detector/platform_detector.dart';
|
||||
export 'src/core/util/extension.dart';
|
||||
export 'src/db/chat_persistence_client.dart';
|
||||
export 'src/event_type.dart';
|
||||
export 'src/permission_type.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 = '5.1.0';
|
||||
const PACKAGE_VERSION = '5.3.0';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: stream_chat
|
||||
homepage: https://getstream.io/
|
||||
description: The official Dart client for Stream Chat, a service for building chat applications.
|
||||
version: 5.1.0
|
||||
version: 5.3.0
|
||||
repository: https://github.com/GetStream/stream-chat-flutter
|
||||
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
||||
|
||||
@@ -9,26 +9,26 @@ environment:
|
||||
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: ^2.0.3
|
||||
http_parser: ^4.0.0
|
||||
jose: ^0.3.2
|
||||
json_annotation: ^4.5.0
|
||||
logging: ^1.0.1
|
||||
meta: ^1.3.0
|
||||
mime: ^1.0.0
|
||||
rate_limiter: ^0.1.1
|
||||
rxdart: ^0.27.0
|
||||
uuid: ^3.0.4
|
||||
web_socket_channel: ^2.0.0
|
||||
async: ^2.10.0
|
||||
collection: ^1.17.0
|
||||
dio: ^5.1.1
|
||||
equatable: ^2.0.5
|
||||
freezed_annotation: ^2.2.0
|
||||
http_parser: ^4.0.2
|
||||
jose: ^0.3.3
|
||||
json_annotation: ^4.8.0
|
||||
logging: ^1.1.1
|
||||
meta: ^1.8.0
|
||||
mime: ^1.0.4
|
||||
rate_limiter: ^1.0.0
|
||||
rxdart: ^0.27.7
|
||||
uuid: ^3.0.7
|
||||
web_socket_channel: ^2.3.0
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.0.1
|
||||
dart_code_metrics: ^4.4.0
|
||||
freezed: ^2.0.3
|
||||
json_serializable: ^6.2.0
|
||||
build_runner: ^2.3.3
|
||||
dart_code_metrics: ^5.7.0
|
||||
freezed: ^2.3.2
|
||||
json_serializable: ^6.6.1
|
||||
mocktail: ^0.3.0
|
||||
test: ^1.17.12
|
||||
test: ^1.24.1
|
||||
|
||||
+25
-101
@@ -1,4 +1,3 @@
|
||||
|
||||
{
|
||||
"channel": {
|
||||
"id": "dev",
|
||||
@@ -18,400 +17,325 @@
|
||||
"text": "fasdfa",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "dry-meadow-0-e8e74482-b4cd-48db-9d1e-30e6c191786f",
|
||||
"text": "test message",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "dry-meadow-0-53e6299f-9b97-4a9c-a27e-7e2dde49b7e0",
|
||||
"text": "test message",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "dry-meadow-0-80925be0-786e-40a5-b225-486518dafd35",
|
||||
"text": "asdfadf",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "dry-meadow-0-64d7970f-ede8-4b31-9738-1bc1756d2bfe",
|
||||
"text": "test",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "withered-cell-0-84cbd760-cf55-4f7e-9207-c5f66cccc6dc",
|
||||
"text": "hi",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "dry-meadow-0-e9203588-43c3-40b1-91f7-f217fc42aa53",
|
||||
"text": "fantastic",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "withered-cell-0-7e3552d7-7a0d-45f2-a856-e91b23a7e240",
|
||||
"text": "nice to meet you",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "dry-meadow-0-1ffeafd4-e4fc-4c84-9394-9d7cb10fff42",
|
||||
"text": "hey",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "dry-meadow-0-3f147324-12c8-4b41-9fb5-2db88d065efa",
|
||||
"text": "hello, everyone",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "dry-meadow-0-51a348ae-0c0a-44de-a556-eac7891c0cf0",
|
||||
"text": "who is there?",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "icy-recipe-7-a29e237b-8d81-4a97-9bc8-d42bca3f1356",
|
||||
"text": "하이",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "icy-recipe-7-935c396e-ddf8-4a9a-951c-0a12fa5bf055",
|
||||
"text": "what are you doing?",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "throbbing-boat-5-1e4d5730-5ff0-4d25-9948-9f34ffda43e4",
|
||||
"text": "👍",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "snowy-credit-3-3e0c1a0d-d22f-42ee-b2a1-f9f49477bf21",
|
||||
"text": "sdasas",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "snowy-credit-3-3319537e-2d0e-4876-8170-a54f046e4b7d",
|
||||
"text": "cjshsa",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "snowy-credit-3-cfaf0b46-1daa-49c5-947c-b16d6697487d",
|
||||
"text": "nhisagdhsadz",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "snowy-credit-3-cebe25a7-a3a3-49fc-9919-91c6725e81f3",
|
||||
"text": "hvadhsahzd",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "divine-glade-9-0cea9262-5766-48e9-8b22-311870aed3bf",
|
||||
"text": "hello",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "red-firefly-9-c4e9007b-bb7d-4238-ae08-5f8e3cd03d73",
|
||||
"text": "hello",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "bitter-glade-2-02aee4eb-4093-4736-808b-2de75820e854",
|
||||
"text": "hello",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "morning-sea-1-0c700bcb-46dd-4224-b590-e77bdbccc480",
|
||||
"text": "http://jaeger.ui.gtstrm.com/",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "ancient-salad-0-53e8b4e6-5b7b-43ad-aeee-8bfb6a9ed0be",
|
||||
"text": "hi",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "ancient-salad-0-8c225075-bd4c-42e2-8024-530aae13cd40",
|
||||
"text": "hi",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
},
|
||||
{
|
||||
"id": "proud-sea-7-17802096-cbf8-4e3c-addd-4ee31f4c8b5c",
|
||||
"text": "😃",
|
||||
"attachments": [],
|
||||
"parent_id": null,
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"show_in_channel": null,
|
||||
"mentioned_users": [],
|
||||
"status": "SENT",
|
||||
"silent": false,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null
|
||||
"pin_expires": null
|
||||
}
|
||||
],
|
||||
"pinned_messages": [],
|
||||
|
||||
@@ -18,12 +18,9 @@
|
||||
],
|
||||
"mentioned_users": [],
|
||||
"parent_id": "parentId",
|
||||
"quoted_message": null,
|
||||
"quoted_message_id": null,
|
||||
"pinned": false,
|
||||
"pinned_at": null,
|
||||
"pin_expires": null,
|
||||
"pinned_by": null,
|
||||
"show_in_channel": true,
|
||||
"hey": "test"
|
||||
}
|
||||
@@ -516,6 +516,9 @@ void main() {
|
||||
});
|
||||
|
||||
setUp(() async {
|
||||
when(() => persistence.updateLastSyncAt(any()))
|
||||
.thenAnswer((_) => Future.value());
|
||||
when(persistence.getLastSyncAt).thenAnswer((_) async => null);
|
||||
client = StreamChatClient(apiKey, chatApi: api, ws: ws)
|
||||
..chatPersistenceClient = persistence;
|
||||
await client.connectUser(user, token);
|
||||
@@ -532,9 +535,12 @@ void main() {
|
||||
test(
|
||||
'''should update persistence connectionInfo and lastSync when sync succeeds''',
|
||||
() async {
|
||||
// persistence.updateLastSyncAt might be called
|
||||
// when connecting the user.
|
||||
// Resetting the logs so we start counting invocations correctly.
|
||||
reset(persistence);
|
||||
const cids = ['test-cid-1', 'test-cid-2', 'test-cid-3'];
|
||||
final lastSyncAt = DateTime.now();
|
||||
|
||||
when(() => api.general.sync(cids, lastSyncAt))
|
||||
.thenAnswer((_) async => SyncResponse()
|
||||
..events = [
|
||||
@@ -567,6 +573,10 @@ void main() {
|
||||
test(
|
||||
'should work fine if persistence contains sync params',
|
||||
() async {
|
||||
// persistence.updateLastSyncAt might be called
|
||||
// when connecting the user.
|
||||
// Resetting the logs so we start counting invocations correctly.
|
||||
reset(persistence);
|
||||
const cids = ['test-cid-1', 'test-cid-2', 'test-cid-3'];
|
||||
final lastSyncAt = DateTime.now();
|
||||
|
||||
|
||||
@@ -22,26 +22,37 @@ void main() {
|
||||
|
||||
test('addDevice should work', () async {
|
||||
const deviceId = 'test-device-id';
|
||||
const pushProvider = PushProvider.firebase;
|
||||
|
||||
const pushProvidersMap = {
|
||||
'apn': PushProvider.apn,
|
||||
'firebase': PushProvider.firebase,
|
||||
'huawei': PushProvider.huawei,
|
||||
'xiaomi': PushProvider.xiaomi,
|
||||
};
|
||||
const path = '/devices';
|
||||
|
||||
when(() => client.post(
|
||||
path,
|
||||
data: {
|
||||
'id': deviceId,
|
||||
'push_provider': pushProvider.name,
|
||||
},
|
||||
))
|
||||
.thenAnswer(
|
||||
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||
for (final pushProviderMapEntry in pushProvidersMap.entries) {
|
||||
final data = {
|
||||
'id': deviceId,
|
||||
'push_provider': pushProviderMapEntry.key,
|
||||
};
|
||||
when(() {
|
||||
return client.post(
|
||||
path,
|
||||
data: data,
|
||||
);
|
||||
}).thenAnswer(
|
||||
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||
|
||||
final res = await deviceApi.addDevice(deviceId, pushProvider);
|
||||
final res =
|
||||
await deviceApi.addDevice(deviceId, pushProviderMapEntry.value);
|
||||
|
||||
expect(res, isNotNull);
|
||||
expect(res, isNotNull);
|
||||
|
||||
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||
verify(() => client.post(path, data: data)).called(1);
|
||||
}
|
||||
verifyNoMoreInteractions(client);
|
||||
expect(pushProvidersMap.length, PushProvider.values.length,
|
||||
reason: 'All PushProvider should be tested');
|
||||
});
|
||||
|
||||
test('addDevice should work with pushProviderName', () async {
|
||||
|
||||
@@ -394,7 +394,12 @@ void main() {
|
||||
path,
|
||||
data: {'language': language},
|
||||
)).thenAnswer((_) async => successResponse(path, data: {
|
||||
'message': translatedMessage.toJson(),
|
||||
'message': {
|
||||
...translatedMessage.toJson(),
|
||||
'i18n': {
|
||||
language: translatedMessageText,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
final res = await messageApi.translateMessage(messageId, language);
|
||||
|
||||
@@ -129,7 +129,11 @@ void main() {
|
||||
await client.get('path');
|
||||
} on StreamChatNetworkError catch (e) {
|
||||
expect(e, isA<StreamChatNetworkError>());
|
||||
expect(e.message, "Dio can't establish new connection after closed.");
|
||||
expect(
|
||||
e.message,
|
||||
"The connection errored: Dio can't establish a new connection"
|
||||
' after it was closed.',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,14 +1,53 @@
|
||||
## Upcoming
|
||||
|
||||
🐞 Fixed
|
||||
|
||||
- [#1462](https://github.com/GetStream/stream-chat-flutter/issues/1462) Fixed support for iPad in the share button for images.
|
||||
- [#1475](https://github.com/GetStream/stream-chat-flutter/issues/1475) Fixed typo to fix compilation.
|
||||
|
||||
✅ Added
|
||||
|
||||
- Now it is possible to customize the max lines of the title of a url attachment. Before it was always 1 line.
|
||||
- Added `attachmentActionsModalBuilder` parameter to `StreamMessageWidget` that allows to customize `AttachmentActionsModal`.
|
||||
|
||||
🔄 Changed
|
||||
|
||||
- Updated dependencies to resolvable versions.
|
||||
|
||||
## 5.3.0
|
||||
|
||||
🔄 Changed
|
||||
|
||||
- Updated `photo_manager` dependency to `^2.5.2`
|
||||
|
||||
🐞 Fixed
|
||||
- [[#1424]](https://github.com/GetStream/stream-chat-flutter/issues/1424) Fixed a render issue when showing messages starting with 4 whitespaces.
|
||||
- Fixed a bug where the `AttachmentPickerBottomSheet` was not able to identify the mobile browser.
|
||||
- Fixed uploading files on Windows - fixed temp file path.
|
||||
|
||||
✅ Added
|
||||
- New `noPhotoOrVideoLabel` displayed when there is no files to choose.
|
||||
|
||||
## 5.2.0
|
||||
|
||||
✅ Added
|
||||
- Added a new `bottomRowBuilderWithDefaultWidget` parameter to `StreamMessageWidget` which contains a third parameter (default `BottomRow` widget with `copyWith` method available) to allow easier customization.
|
||||
|
||||
🔄 Changed
|
||||
|
||||
- Updated `lottie` dependency to `^2.0.0`
|
||||
- Updated `desktop_drop` dependency to `^0.4.0`
|
||||
- Updated `connectivity_plus` dependency to `^3.0.2`
|
||||
- Updated `dart_vlc` dependency to `^0.4.0`
|
||||
- Updated `file_picker` dependency to `^5.2.4`
|
||||
- Deprecated `StreamMessageWidget.bottomRowBuilder` in favor of `StreamMessageWidget.bottomRowBuilderWithDefaultWidget`.
|
||||
- Deprecated `StreamMessageWidget.deletedBottomRowBuilder` in favor of `StreamMessageWidget.bottomRowBuilderWithDefaultWidget`.
|
||||
- Deprecated `StreamMessageWidget.usernameBuilder` in favor of `StreamMessageWidget.bottomRowBuilderWithDefaultWidget`.
|
||||
|
||||
🐞 Fixed
|
||||
- [[#1379]](https://github.com/GetStream/stream-chat-flutter/issues/1379) Fixed "Issues with photo attachments on web", where the cached image attachment would not render while uploading.
|
||||
- Fix render overflow issue with `MessageSearchListTileTitle`. It now uses `Text.rich` instead of `Row`. Better default behaviour and allows `TextOverflow`.
|
||||
- [[1346]](https://github.com/GetStream/stream-chat-flutter/issues/1346) Fixed a render issue while uploading video on web.
|
||||
- [[#1347]](https://github.com/GetStream/stream-chat-flutter/issues/1347) `onReply` not working in `AttachmentActionsModal` which is used by `StreamImageAttachment` and `StreamImageGroup`.
|
||||
|
||||
## 5.1.0
|
||||
@@ -1317,4 +1356,4 @@ The property showVideoFullScreen was added mainly because of this issue brianega
|
||||
|
||||
## 0.0.1
|
||||
|
||||
- First release
|
||||
- First release
|
||||
|
||||
@@ -48,6 +48,7 @@ You should then run `flutter packages get`
|
||||
|
||||
The package uses [`photo_manager`](https://pub.dev/packages/photo_manager) to access the device's photo library.
|
||||
Follow [this wiki](https://pub.dev/packages/photo_manager#android-10-q-29) to fulfil the Android requirements.
|
||||
You need to take additional steps if you are targeting Android 13. Read [this section](https://pub.dev/packages/photo_manager#android-13-api-33-extra-configs) for more information.
|
||||
|
||||
### iOS
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
compileSdkVersion 33
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
@@ -41,7 +41,7 @@ android {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.example.example"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 31
|
||||
targetSdkVersion 33
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
@@ -9,17 +9,20 @@
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||
<application
|
||||
android:name=".Application"
|
||||
android:label="example"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="example">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:exported="true"
|
||||
android:hardwareAccelerated="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:exported="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.6.0'
|
||||
ext.kotlin_version = '1.7.21'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.2.2'
|
||||
classpath 'com.android.tools.build:gradle:7.2.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 51;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@@ -199,6 +199,7 @@
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
@@ -235,6 +236,7 @@
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
|
||||
@@ -61,5 +61,7 @@
|
||||
<string>Explain why your app uses the mic</string>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -177,7 +177,7 @@ class _SplitViewState extends State<SplitView> {
|
||||
: Center(
|
||||
child: Text(
|
||||
'Pick a channel to show the messages 💬',
|
||||
style: Theme.of(context).textTheme.headline5,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -78,7 +78,7 @@ class _SplitViewState extends State<SplitView> {
|
||||
: Center(
|
||||
child: Text(
|
||||
'Pick a channel to show the messages 💬',
|
||||
style: Theme.of(context).textTheme.headline5,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -74,6 +74,9 @@ class MyApp extends StatelessWidget {
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
ownMessageTheme: const StreamMessageThemeData(
|
||||
urlAttachmentTitleMaxLine: 1,
|
||||
),
|
||||
otherMessageTheme: StreamMessageThemeData(
|
||||
messageBackgroundColor: colorTheme.textHighEmphasis,
|
||||
messageTextStyle: TextStyle(
|
||||
@@ -82,6 +85,7 @@ class MyApp extends StatelessWidget {
|
||||
avatarTheme: StreamAvatarThemeData(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
urlAttachmentTitleMaxLine: 1,
|
||||
),
|
||||
).merge(defaultTheme);
|
||||
|
||||
|
||||
@@ -9,10 +9,8 @@
|
||||
#include <dart_vlc/dart_vlc_plugin.h>
|
||||
#include <desktop_drop/desktop_drop_plugin.h>
|
||||
#include <file_selector_linux/file_selector_plugin.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) dart_vlc_registrar =
|
||||
@@ -24,16 +22,10 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
||||
g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin");
|
||||
sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar);
|
||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) window_manager_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
|
||||
window_manager_plugin_register_with_registrar(window_manager_registrar);
|
||||
}
|
||||
|
||||
@@ -6,10 +6,8 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
dart_vlc
|
||||
desktop_drop
|
||||
file_selector_linux
|
||||
screen_retriever
|
||||
sqlite3_flutter_libs
|
||||
url_launcher_linux
|
||||
window_manager
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 51;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXAggregateTarget section */
|
||||
@@ -203,7 +203,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0920;
|
||||
LastUpgradeCheck = 0930;
|
||||
LastUpgradeCheck = 1300;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
33CC10EC2044A3C60003C045 = {
|
||||
@@ -256,6 +256,7 @@
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
@@ -404,7 +405,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
@@ -484,7 +485,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
@@ -531,7 +532,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1000"
|
||||
LastUpgradeVersion = "1300"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -19,6 +19,7 @@ version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: '>=2.17.0 <3.0.0'
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
dependencies:
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
@@ -27,7 +28,7 @@ dependencies:
|
||||
cupertino_icons: ^1.0.4
|
||||
flutter:
|
||||
sdk: flutter
|
||||
responsive_builder: ^0.4.2
|
||||
responsive_builder: ^0.6.4
|
||||
stream_chat_flutter:
|
||||
path: ../
|
||||
stream_chat_localizations:
|
||||
|
||||
+4
-10
@@ -6,16 +6,14 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <connectivity_plus_windows/connectivity_plus_windows_plugin.h>
|
||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||
#include <dart_vlc/dart_vlc_plugin.h>
|
||||
#include <desktop_drop/desktop_drop_plugin.h>
|
||||
#include <file_selector_windows/file_selector_windows.h>
|
||||
#include <flutter_native_view/flutter_native_view_plugin.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
|
||||
#include <thumblr_windows/thumblr_windows_plugin.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||
@@ -26,16 +24,12 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
registry->GetRegistrarForPlugin("DesktopDropPlugin"));
|
||||
FileSelectorWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||
FlutterNativeViewPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterNativeViewPlugin"));
|
||||
ScreenRetrieverPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
||||
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
||||
Sqlite3FlutterLibsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin"));
|
||||
ThumblrWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ThumblrWindowsPlugin"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
WindowManagerPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
|
||||
}
|
||||
|
||||
@@ -3,16 +3,14 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
connectivity_plus_windows
|
||||
connectivity_plus
|
||||
dart_vlc
|
||||
desktop_drop
|
||||
file_selector_windows
|
||||
flutter_native_view
|
||||
screen_retriever
|
||||
share_plus
|
||||
sqlite3_flutter_libs
|
||||
thumblr_windows
|
||||
url_launcher_windows
|
||||
window_manager
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export 'attachment_error.dart';
|
||||
export 'attachment_error.dart';
|
||||
export 'attachment_upload_state_builder.dart';
|
||||
export 'attachment_widget.dart' show AttachmentSource;
|
||||
export 'file_attachment.dart';
|
||||
|
||||
@@ -195,7 +195,7 @@ class _FileTypeImage extends StatelessWidget {
|
||||
shape: _getDefaultShape(context),
|
||||
child: source.when(
|
||||
local: () => StreamVideoThumbnailImage(
|
||||
video: attachment.file!.path!,
|
||||
video: attachment.file!.path,
|
||||
placeholderBuilder: (_) => const Center(
|
||||
child: SizedBox(
|
||||
width: 20,
|
||||
@@ -205,7 +205,7 @@ class _FileTypeImage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
network: () => StreamVideoThumbnailImage(
|
||||
video: attachment.assetUrl!,
|
||||
video: attachment.assetUrl,
|
||||
placeholderBuilder: (_) => const Center(
|
||||
child: SizedBox(
|
||||
width: 20,
|
||||
|
||||
@@ -17,6 +17,7 @@ class StreamGiphyAttachment extends StreamAttachmentWidget {
|
||||
this.onShowMessage,
|
||||
this.onReplyMessage,
|
||||
this.onAttachmentTap,
|
||||
this.attachmentActionsModalBuilder,
|
||||
});
|
||||
|
||||
/// {@macro showMessageCallback}
|
||||
@@ -28,6 +29,9 @@ class StreamGiphyAttachment extends StreamAttachmentWidget {
|
||||
/// {@macro onAttachmentTap}
|
||||
final OnAttachmentTap? onAttachmentTap;
|
||||
|
||||
/// {@macro attachmentActionsBuilder}
|
||||
final AttachmentActionsBuilder? attachmentActionsModalBuilder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final imageUrl =
|
||||
@@ -260,6 +264,7 @@ class StreamGiphyAttachment extends StreamAttachmentWidget {
|
||||
userName: message.user!.name,
|
||||
onShowMessage: onShowMessage,
|
||||
onReplyMessage: onReplyMessage,
|
||||
attachmentActionsModalBuilder: attachmentActionsModalBuilder,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
+3
-1
@@ -123,7 +123,9 @@ class StreamAttachmentHandler extends StreamAttachmentHandlerBase {
|
||||
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final tempPath = Uri.file(tempDir.path, windows: CurrentPlatform.isWindows);
|
||||
final tempFilePath = tempPath.resolve(fileName!).path;
|
||||
final tempFilePath = tempPath
|
||||
.resolve(fileName!)
|
||||
.toFilePath(windows: CurrentPlatform.isWindows);
|
||||
print(tempFilePath);
|
||||
|
||||
final attachmentFileBytes = attachmentFile.bytes;
|
||||
|
||||
@@ -22,6 +22,7 @@ class StreamImageAttachment extends StreamAttachmentWidget {
|
||||
this.imageThumbnailSize = const Size(400, 400),
|
||||
this.imageThumbnailResizeType = 'clip',
|
||||
this.imageThumbnailCropType = 'center',
|
||||
this.attachmentActionsModalBuilder,
|
||||
});
|
||||
|
||||
/// The [StreamMessageThemeData] to use for the image title
|
||||
@@ -52,6 +53,9 @@ class StreamImageAttachment extends StreamAttachmentWidget {
|
||||
/// Defaults to [center]
|
||||
final String /*center|top|bottom|left|right*/ imageThumbnailCropType;
|
||||
|
||||
/// {@macro attachmentActionsBuilder}
|
||||
final AttachmentActionsBuilder? attachmentActionsModalBuilder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return source.when(
|
||||
@@ -161,6 +165,8 @@ class StreamImageAttachment extends StreamAttachmentWidget {
|
||||
userName: message.user!.name,
|
||||
onShowMessage: onShowMessage,
|
||||
onReplyMessage: onReplyMessage,
|
||||
attachmentActionsModalBuilder:
|
||||
attachmentActionsModalBuilder,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@ class StreamImageGroup extends StatelessWidget {
|
||||
this.imageThumbnailSize = const Size(400, 400),
|
||||
this.imageThumbnailResizeType = 'clip',
|
||||
this.imageThumbnailCropType = 'center',
|
||||
this.attachmentActionsModalBuilder,
|
||||
});
|
||||
|
||||
/// List of attachments to show
|
||||
@@ -54,6 +55,9 @@ class StreamImageGroup extends StatelessWidget {
|
||||
/// Defaults to [center]
|
||||
final String /*center|top|bottom|left|right*/ imageThumbnailCropType;
|
||||
|
||||
/// {@macro attachmentActionsBuilder}
|
||||
final AttachmentActionsBuilder? attachmentActionsModalBuilder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ConstrainedBox(
|
||||
@@ -154,6 +158,7 @@ class StreamImageGroup extends StatelessWidget {
|
||||
userName: message.user!.name,
|
||||
onShowMessage: onShowMessage,
|
||||
onReplyMessage: onReplyMessage,
|
||||
attachmentActionsModalBuilder: attachmentActionsModalBuilder,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -170,6 +175,7 @@ class StreamImageGroup extends StatelessWidget {
|
||||
imageThumbnailSize: imageThumbnailSize,
|
||||
imageThumbnailResizeType: imageThumbnailResizeType,
|
||||
imageThumbnailCropType: imageThumbnailCropType,
|
||||
attachmentActionsModalBuilder: attachmentActionsModalBuilder,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ class StreamUrlAttachment extends StatelessWidget {
|
||||
if (urlAttachment.title != null)
|
||||
Text(
|
||||
urlAttachment.title!.trim(),
|
||||
maxLines: 1,
|
||||
maxLines: messageTheme.urlAttachmentTitleMaxLine ?? 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: chatThemeData.textTheme.body
|
||||
.copyWith(fontWeight: FontWeight.w700),
|
||||
|
||||
@@ -17,6 +17,7 @@ class StreamVideoAttachment extends StreamAttachmentWidget {
|
||||
this.onShowMessage,
|
||||
this.onReplyMessage,
|
||||
this.onAttachmentTap,
|
||||
this.attachmentActionsModalBuilder,
|
||||
});
|
||||
|
||||
/// The [StreamMessageThemeData] to use for the title
|
||||
@@ -31,6 +32,9 @@ class StreamVideoAttachment extends StreamAttachmentWidget {
|
||||
/// {@macro onAttachmentTap}
|
||||
final OnAttachmentTap? onAttachmentTap;
|
||||
|
||||
/// {@macro attachmentActionsBuilder}
|
||||
final AttachmentActionsBuilder? attachmentActionsModalBuilder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return source.when(
|
||||
@@ -41,7 +45,7 @@ class StreamVideoAttachment extends StreamAttachmentWidget {
|
||||
return _buildVideoAttachment(
|
||||
context,
|
||||
StreamVideoThumbnailImage(
|
||||
video: attachment.file!.path!,
|
||||
video: attachment.file!.path,
|
||||
thumbUrl: attachment.thumbUrl,
|
||||
constraints: constraints,
|
||||
),
|
||||
@@ -54,7 +58,7 @@ class StreamVideoAttachment extends StreamAttachmentWidget {
|
||||
return _buildVideoAttachment(
|
||||
context,
|
||||
StreamVideoThumbnailImage(
|
||||
video: attachment.assetUrl!,
|
||||
video: attachment.assetUrl,
|
||||
thumbUrl: attachment.thumbUrl,
|
||||
constraints: constraints,
|
||||
),
|
||||
@@ -86,6 +90,8 @@ class StreamVideoAttachment extends StreamAttachmentWidget {
|
||||
userName: message.user!.name,
|
||||
onShowMessage: onShowMessage,
|
||||
onReplyMessage: onReplyMessage,
|
||||
attachmentActionsModalBuilder:
|
||||
attachmentActionsModalBuilder,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -162,8 +162,8 @@ class StreamChannelHeader extends StatelessWidget
|
||||
showMessage: showConnectionStateTile && showStatus,
|
||||
message: statusString,
|
||||
child: AppBar(
|
||||
toolbarTextStyle: theme.textTheme.bodyText2,
|
||||
titleTextStyle: theme.textTheme.headline6,
|
||||
toolbarTextStyle: theme.textTheme.bodyMedium,
|
||||
titleTextStyle: theme.textTheme.titleLarge,
|
||||
systemOverlayStyle: theme.brightness == Brightness.dark
|
||||
? SystemUiOverlayStyle.light
|
||||
: SystemUiOverlayStyle.dark,
|
||||
|
||||
@@ -131,8 +131,8 @@ class StreamChannelListHeader extends StatelessWidget
|
||||
showMessage: showConnectionStateTile && showStatus,
|
||||
message: statusString,
|
||||
child: AppBar(
|
||||
toolbarTextStyle: theme.textTheme.bodyText2,
|
||||
titleTextStyle: theme.textTheme.headline6,
|
||||
toolbarTextStyle: theme.textTheme.bodyMedium,
|
||||
titleTextStyle: theme.textTheme.titleLarge,
|
||||
systemOverlayStyle: theme.brightness == Brightness.dark
|
||||
? SystemUiOverlayStyle.light
|
||||
: SystemUiOverlayStyle.dark,
|
||||
@@ -181,7 +181,10 @@ class StreamChannelListHeader extends StatelessWidget
|
||||
package: 'stream_chat_flutter',
|
||||
width: 24,
|
||||
height: 24,
|
||||
color: color,
|
||||
colorFilter: ColorFilter.mode(
|
||||
color,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:file_selector/file_selector.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
@@ -61,6 +60,7 @@ class StreamGalleryFooter extends StatefulWidget
|
||||
|
||||
class _StreamGalleryFooterState extends State<StreamGalleryFooter> {
|
||||
final shareButtonKey = GlobalKey();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const showShareButton = !kIsWeb;
|
||||
@@ -108,12 +108,19 @@ class _StreamGalleryFooterState extends State<StreamGalleryFooter> {
|
||||
await file.writeAsBytes(bytes);
|
||||
final box =
|
||||
shareButtonKey.currentContext?.findRenderObject();
|
||||
final size = shareButtonKey.currentContext?.size;
|
||||
|
||||
final position =
|
||||
(box! as RenderBox).localToGlobal(Offset.zero);
|
||||
|
||||
await Share.shareXFiles(
|
||||
[XFile(filePath)],
|
||||
sharePositionOrigin:
|
||||
Rect.fromLTWH(position.dx, position.dy, 0, 0),
|
||||
sharePositionOrigin: Rect.fromLTWH(
|
||||
position.dx,
|
||||
position.dy,
|
||||
size?.width ?? 50,
|
||||
(size?.height ?? 2) / 2,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -219,8 +226,8 @@ class _StreamGalleryFooterState extends State<StreamGalleryFooter> {
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: StreamVideoThumbnailImage(
|
||||
video: (attachment.file?.path ??
|
||||
attachment.assetUrl)!,
|
||||
video:
|
||||
attachment.file?.path ?? attachment.assetUrl,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -80,8 +80,8 @@ class StreamGalleryHeader extends StatelessWidget
|
||||
final galleryHeaderThemeData = StreamGalleryHeaderTheme.of(context);
|
||||
final theme = Theme.of(context);
|
||||
return AppBar(
|
||||
toolbarTextStyle: theme.textTheme.bodyText2,
|
||||
titleTextStyle: theme.textTheme.headline6,
|
||||
toolbarTextStyle: theme.textTheme.bodyMedium,
|
||||
titleTextStyle: theme.textTheme.titleLarge,
|
||||
systemOverlayStyle: theme.brightness == Brightness.dark
|
||||
? SystemUiOverlayStyle.light
|
||||
: SystemUiOverlayStyle.dark,
|
||||
|
||||
@@ -18,6 +18,9 @@ abstract class Translations {
|
||||
/// The label for showing no users
|
||||
String get noUsersLabel;
|
||||
|
||||
/// The label for showing no photo or video
|
||||
String get noPhotoOrVideoLabel;
|
||||
|
||||
/// The text for showing user is online
|
||||
String get userOnlineText;
|
||||
|
||||
@@ -78,7 +81,10 @@ abstract class Translations {
|
||||
|
||||
/// The text for showing the unread messages count
|
||||
/// in the [StreamMessageListView]
|
||||
String unreadMessagesSeparatorText(int unreadCount);
|
||||
String unreadMessagesSeparatorText(
|
||||
@Deprecated('unreadCount is not used anymore and will be removed ')
|
||||
int unreadCount,
|
||||
);
|
||||
|
||||
/// The label for "connected" in [StreamConnectionStatusBuilder]
|
||||
String get connectedLabel;
|
||||
@@ -373,6 +379,9 @@ class DefaultTranslations implements Translations {
|
||||
@override
|
||||
String get noUsersLabel => 'There are no users currently';
|
||||
|
||||
@override
|
||||
String get noPhotoOrVideoLabel => 'There is no photo or video';
|
||||
|
||||
@override
|
||||
String get retryLabel => 'Retry';
|
||||
|
||||
@@ -791,12 +800,7 @@ Attachment limit exceeded: it's not possible to add more than $limit attachments
|
||||
String get linkDisabledError => 'Links are disabled';
|
||||
|
||||
@override
|
||||
String unreadMessagesSeparatorText(int unreadCount) {
|
||||
if (unreadCount == 1) {
|
||||
return '1 unread message';
|
||||
}
|
||||
return '$unreadCount unread messages';
|
||||
}
|
||||
String unreadMessagesSeparatorText(int unreadCount) => 'New messages';
|
||||
|
||||
@override
|
||||
String get enableFileAccessMessage => 'Please enable access to files'
|
||||
|
||||
+29
-39
@@ -1,7 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart' show kIsWeb, defaultTargetPlatform;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/platform_widget_builder/src/platform_widget.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
/// Shows a modal material design bottom sheet.
|
||||
@@ -107,44 +107,34 @@ Future<T?> showStreamAttachmentPickerModalBottomSheet<T>({
|
||||
controller: controller,
|
||||
initialAttachments: initialAttachments,
|
||||
builder: (context, controller, child) {
|
||||
return PlatformWidget(
|
||||
web: (context) {
|
||||
return webOrDesktopAttachmentPickerBuilder.call(
|
||||
context: context,
|
||||
controller: controller,
|
||||
customOptions: customOptions?.map(
|
||||
WebOrDesktopAttachmentPickerOption.fromAttachmentPickerOption,
|
||||
),
|
||||
attachmentThumbnailSize: attachmentThumbnailSize,
|
||||
attachmentThumbnailFormat: attachmentThumbnailFormat,
|
||||
attachmentThumbnailQuality: attachmentThumbnailQuality,
|
||||
attachmentThumbnailScale: attachmentThumbnailScale,
|
||||
);
|
||||
},
|
||||
mobile: (context) {
|
||||
return mobileAttachmentPickerBuilder.call(
|
||||
context: context,
|
||||
controller: controller,
|
||||
customOptions: customOptions,
|
||||
attachmentThumbnailSize: attachmentThumbnailSize,
|
||||
attachmentThumbnailFormat: attachmentThumbnailFormat,
|
||||
attachmentThumbnailQuality: attachmentThumbnailQuality,
|
||||
attachmentThumbnailScale: attachmentThumbnailScale,
|
||||
);
|
||||
},
|
||||
desktop: (context) {
|
||||
return webOrDesktopAttachmentPickerBuilder.call(
|
||||
context: context,
|
||||
controller: controller,
|
||||
customOptions: customOptions?.map(
|
||||
WebOrDesktopAttachmentPickerOption.fromAttachmentPickerOption,
|
||||
),
|
||||
attachmentThumbnailSize: attachmentThumbnailSize,
|
||||
attachmentThumbnailFormat: attachmentThumbnailFormat,
|
||||
attachmentThumbnailQuality: attachmentThumbnailQuality,
|
||||
attachmentThumbnailScale: attachmentThumbnailScale,
|
||||
);
|
||||
},
|
||||
final currentPlatform = defaultTargetPlatform;
|
||||
final isWebOrDesktop = kIsWeb ||
|
||||
currentPlatform == TargetPlatform.macOS ||
|
||||
currentPlatform == TargetPlatform.linux ||
|
||||
currentPlatform == TargetPlatform.windows;
|
||||
|
||||
if (isWebOrDesktop) {
|
||||
return webOrDesktopAttachmentPickerBuilder.call(
|
||||
context: context,
|
||||
controller: controller,
|
||||
customOptions: customOptions?.map(
|
||||
WebOrDesktopAttachmentPickerOption.fromAttachmentPickerOption,
|
||||
),
|
||||
attachmentThumbnailSize: attachmentThumbnailSize,
|
||||
attachmentThumbnailFormat: attachmentThumbnailFormat,
|
||||
attachmentThumbnailQuality: attachmentThumbnailQuality,
|
||||
attachmentThumbnailScale: attachmentThumbnailScale,
|
||||
);
|
||||
}
|
||||
|
||||
return mobileAttachmentPickerBuilder.call(
|
||||
context: context,
|
||||
controller: controller,
|
||||
customOptions: customOptions,
|
||||
attachmentThumbnailSize: attachmentThumbnailSize,
|
||||
attachmentThumbnailFormat: attachmentThumbnailFormat,
|
||||
attachmentThumbnailQuality: attachmentThumbnailQuality,
|
||||
attachmentThumbnailScale: attachmentThumbnailScale,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -276,7 +276,7 @@ class _ParseAttachments extends StatelessWidget {
|
||||
'video': (_, attachment) {
|
||||
return StreamVideoThumbnailImage(
|
||||
key: ValueKey(attachment.assetUrl),
|
||||
video: attachment.file?.path ?? attachment.assetUrl!,
|
||||
video: attachment.file?.path ?? attachment.assetUrl,
|
||||
constraints: BoxConstraints.loose(const Size(32, 32)),
|
||||
errorBuilder: (_, __) => AttachmentError(
|
||||
constraints: BoxConstraints.loose(const Size(32, 32)),
|
||||
|
||||
@@ -1194,7 +1194,7 @@ class StreamMessageInputState extends State<StreamMessageInput>
|
||||
104,
|
||||
),
|
||||
),
|
||||
video: (attachment.file?.path ?? attachment.assetUrl)!,
|
||||
video: attachment.file?.path ?? attachment.assetUrl,
|
||||
),
|
||||
Positioned(
|
||||
left: 8,
|
||||
|
||||
@@ -80,7 +80,6 @@ class StreamMessageTextField extends StatefulWidget {
|
||||
this.textAlignVertical,
|
||||
this.textDirection,
|
||||
this.readOnly = false,
|
||||
ToolbarOptions? toolbarOptions,
|
||||
this.showCursor,
|
||||
this.autofocus = false,
|
||||
this.obscuringCharacter = '•',
|
||||
@@ -155,31 +154,7 @@ class StreamMessageTextField extends StatefulWidget {
|
||||
keyboardType = keyboardType ??
|
||||
(maxLines == 1 ? TextInputType.text : TextInputType.multiline),
|
||||
enableInteractiveSelection =
|
||||
enableInteractiveSelection ?? (!readOnly || !obscureText),
|
||||
toolbarOptions = toolbarOptions ??
|
||||
(obscureText
|
||||
? (readOnly
|
||||
// No point in even offering "Select All" in a read-only obscured
|
||||
// field.
|
||||
? const ToolbarOptions()
|
||||
// Writable, but obscured.
|
||||
: const ToolbarOptions(
|
||||
selectAll: true,
|
||||
paste: true,
|
||||
))
|
||||
: (readOnly
|
||||
// Read-only, not obscured.
|
||||
? const ToolbarOptions(
|
||||
selectAll: true,
|
||||
copy: true,
|
||||
)
|
||||
// Writable, not obscured.
|
||||
: const ToolbarOptions(
|
||||
copy: true,
|
||||
cut: true,
|
||||
selectAll: true,
|
||||
paste: true,
|
||||
)));
|
||||
enableInteractiveSelection ?? (!readOnly || !obscureText);
|
||||
|
||||
/// Controls the message being edited.
|
||||
///
|
||||
@@ -304,13 +279,6 @@ class StreamMessageTextField extends StatefulWidget {
|
||||
/// {@macro flutter.widgets.editableText.readOnly}
|
||||
final bool readOnly;
|
||||
|
||||
/// Configuration of toolbar options.
|
||||
///
|
||||
/// If not set, select all and paste will default to be enabled. Copy and cut
|
||||
/// will be disabled if [obscureText] is true. If [readOnly] is true,
|
||||
/// paste and cut will be disabled regardless.
|
||||
final ToolbarOptions toolbarOptions;
|
||||
|
||||
/// {@macro flutter.widgets.editableText.showCursor}
|
||||
final bool? showCursor;
|
||||
|
||||
@@ -720,7 +688,6 @@ class _StreamMessageTextFieldState extends State<StreamMessageTextField>
|
||||
textAlignVertical: widget.textAlignVertical,
|
||||
textDirection: widget.textDirection,
|
||||
readOnly: widget.readOnly,
|
||||
toolbarOptions: widget.toolbarOptions,
|
||||
showCursor: widget.showCursor,
|
||||
autofocus: widget.autofocus,
|
||||
obscuringCharacter: widget.obscuringCharacter,
|
||||
|
||||
+1
-3
@@ -24,9 +24,7 @@ class UnreadMessagesSeparator extends StatelessWidget {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
context.translations.unreadMessagesSeparatorText(
|
||||
unreadCount,
|
||||
),
|
||||
context.translations.unreadMessagesSeparatorText(unreadCount),
|
||||
textAlign: TextAlign.center,
|
||||
style: StreamChannelHeaderTheme.of(context).subtitleStyle,
|
||||
),
|
||||
|
||||
@@ -32,6 +32,7 @@ class BottomRow extends StatelessWidget {
|
||||
this.deletedBottomRowBuilder,
|
||||
this.onThreadTap,
|
||||
this.usernameBuilder,
|
||||
this.sendingIndicatorBuilder,
|
||||
});
|
||||
|
||||
/// {@macro messageIsDeleted}
|
||||
@@ -88,6 +89,61 @@ class BottomRow extends StatelessWidget {
|
||||
/// {@macro usernameBuilder}
|
||||
final Widget Function(BuildContext, Message)? usernameBuilder;
|
||||
|
||||
/// {@macro sendingIndicatorBuilder}
|
||||
final Widget Function(BuildContext, Message)? sendingIndicatorBuilder;
|
||||
|
||||
/// {@template copyWith}
|
||||
/// Creates a copy of [BottomRow] with specified attributes
|
||||
/// overridden.
|
||||
/// {@endtemplate}
|
||||
BottomRow copyWith({
|
||||
Key? key,
|
||||
bool? isDeleted,
|
||||
Message? message,
|
||||
bool? showThreadReplyIndicator,
|
||||
bool? showInChannel,
|
||||
bool? showTimeStamp,
|
||||
bool? showUsername,
|
||||
bool? reverse,
|
||||
bool? showSendingIndicator,
|
||||
bool? hasUrlAttachments,
|
||||
bool? isGiphy,
|
||||
bool? isOnlyEmoji,
|
||||
StreamMessageThemeData? messageTheme,
|
||||
StreamChatThemeData? streamChatTheme,
|
||||
bool? hasNonUrlAttachments,
|
||||
StreamChatState? streamChat,
|
||||
Widget Function(BuildContext, Message)? deletedBottomRowBuilder,
|
||||
void Function(Message)? onThreadTap,
|
||||
Widget Function(BuildContext, Message)? usernameBuilder,
|
||||
Widget Function(BuildContext, Message)? sendingIndicatorBuilder,
|
||||
}) =>
|
||||
BottomRow(
|
||||
key: key ?? this.key,
|
||||
isDeleted: isDeleted ?? this.isDeleted,
|
||||
message: message ?? this.message,
|
||||
showThreadReplyIndicator:
|
||||
showThreadReplyIndicator ?? this.showThreadReplyIndicator,
|
||||
showInChannel: showInChannel ?? this.showInChannel,
|
||||
showTimeStamp: showTimeStamp ?? this.showTimeStamp,
|
||||
showUsername: showUsername ?? this.showUsername,
|
||||
reverse: reverse ?? this.reverse,
|
||||
showSendingIndicator: showSendingIndicator ?? this.showSendingIndicator,
|
||||
hasUrlAttachments: hasUrlAttachments ?? this.hasUrlAttachments,
|
||||
isGiphy: isGiphy ?? this.isGiphy,
|
||||
isOnlyEmoji: isOnlyEmoji ?? this.isOnlyEmoji,
|
||||
messageTheme: messageTheme ?? this.messageTheme,
|
||||
streamChatTheme: streamChatTheme ?? this.streamChatTheme,
|
||||
hasNonUrlAttachments: hasNonUrlAttachments ?? this.hasNonUrlAttachments,
|
||||
streamChat: streamChat ?? this.streamChat,
|
||||
deletedBottomRowBuilder:
|
||||
deletedBottomRowBuilder ?? this.deletedBottomRowBuilder,
|
||||
onThreadTap: onThreadTap ?? this.onThreadTap,
|
||||
usernameBuilder: usernameBuilder ?? this.usernameBuilder,
|
||||
sendingIndicatorBuilder:
|
||||
sendingIndicatorBuilder ?? this.sendingIndicatorBuilder,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (isDeleted) {
|
||||
@@ -147,13 +203,14 @@ class BottomRow extends StatelessWidget {
|
||||
),
|
||||
if (showSendingIndicator)
|
||||
WidgetSpan(
|
||||
child: SendingIndicatorWrapper(
|
||||
messageTheme: messageTheme,
|
||||
message: message,
|
||||
hasNonUrlAttachments: hasNonUrlAttachments,
|
||||
streamChat: streamChat,
|
||||
streamChatTheme: streamChatTheme,
|
||||
),
|
||||
child: sendingIndicatorBuilder?.call(context, message) ??
|
||||
SendingIndicatorWrapper(
|
||||
messageTheme: messageTheme,
|
||||
message: message,
|
||||
hasNonUrlAttachments: hasNonUrlAttachments,
|
||||
streamChat: streamChat,
|
||||
streamChatTheme: streamChatTheme,
|
||||
),
|
||||
),
|
||||
]);
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ class StreamMessageText extends StatelessWidget {
|
||||
.translate(language)
|
||||
.replaceMentions()
|
||||
.text
|
||||
?.replaceAll('\n', '\n\n');
|
||||
?.replaceAll('\n', '\n\n')
|
||||
.trim();
|
||||
final themeData = Theme.of(context);
|
||||
return MarkdownBody(
|
||||
data: messageText ?? '',
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:stream_chat_flutter/src/context_menu_items/context_menu_reaction
|
||||
import 'package:stream_chat_flutter/src/context_menu_items/stream_chat_context_menu_item.dart';
|
||||
import 'package:stream_chat_flutter/src/dialogs/dialogs.dart';
|
||||
import 'package:stream_chat_flutter/src/message_actions_modal/message_actions_modal.dart';
|
||||
import 'package:stream_chat_flutter/src/message_widget/bottom_row.dart';
|
||||
import 'package:stream_chat_flutter/src/message_widget/message_widget_content.dart';
|
||||
import 'package:stream_chat_flutter/src/message_widget/reactions/message_reactions_modal.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
@@ -79,8 +80,15 @@ class StreamMessageWidget extends StatefulWidget {
|
||||
this.userAvatarBuilder,
|
||||
this.editMessageInputBuilder,
|
||||
this.textBuilder,
|
||||
this.bottomRowBuilder,
|
||||
this.deletedBottomRowBuilder,
|
||||
@Deprecated('''
|
||||
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||
Will be removed in the next major version.
|
||||
''') this.bottomRowBuilder,
|
||||
this.bottomRowBuilderWithDefaultWidget,
|
||||
@Deprecated('''
|
||||
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||
Will be removed in the next major version.
|
||||
''') this.deletedBottomRowBuilder,
|
||||
this.customAttachmentBuilders,
|
||||
this.padding,
|
||||
this.textPadding = const EdgeInsets.symmetric(
|
||||
@@ -92,11 +100,19 @@ class StreamMessageWidget extends StatefulWidget {
|
||||
this.onQuotedMessageTap,
|
||||
this.customActions = const [],
|
||||
this.onAttachmentTap,
|
||||
this.usernameBuilder,
|
||||
@Deprecated('''
|
||||
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||
Will be removed in the next major version.
|
||||
''') this.usernameBuilder,
|
||||
this.imageAttachmentThumbnailSize = const Size(400, 400),
|
||||
this.imageAttachmentThumbnailResizeType = 'clip',
|
||||
this.imageAttachmentThumbnailCropType = 'center',
|
||||
}) : attachmentBuilders = {
|
||||
this.attachmentActionsModalBuilder,
|
||||
}) : assert(
|
||||
bottomRowBuilder == null || bottomRowBuilderWithDefaultWidget == null,
|
||||
'You can only use one of the two bottom row builders',
|
||||
),
|
||||
attachmentBuilders = {
|
||||
'image': (context, message, attachments) {
|
||||
final border = RoundedRectangleBorder(
|
||||
side: attachmentBorderSide ??
|
||||
@@ -129,6 +145,8 @@ class StreamMessageWidget extends StatefulWidget {
|
||||
imageThumbnailResizeType:
|
||||
imageAttachmentThumbnailResizeType,
|
||||
imageThumbnailCropType: imageAttachmentThumbnailCropType,
|
||||
attachmentActionsModalBuilder:
|
||||
attachmentActionsModalBuilder,
|
||||
),
|
||||
),
|
||||
attachmentShape: border,
|
||||
@@ -156,6 +174,7 @@ class StreamMessageWidget extends StatefulWidget {
|
||||
imageThumbnailSize: imageAttachmentThumbnailSize,
|
||||
imageThumbnailResizeType: imageAttachmentThumbnailResizeType,
|
||||
imageThumbnailCropType: imageAttachmentThumbnailCropType,
|
||||
attachmentActionsModalBuilder: attachmentActionsModalBuilder,
|
||||
),
|
||||
attachmentShape: border,
|
||||
);
|
||||
@@ -189,6 +208,8 @@ class StreamMessageWidget extends StatefulWidget {
|
||||
onAttachmentTap(message, attachment);
|
||||
}
|
||||
: null,
|
||||
attachmentActionsModalBuilder:
|
||||
attachmentActionsModalBuilder,
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
@@ -306,7 +327,13 @@ class StreamMessageWidget extends StatefulWidget {
|
||||
/// {@template bottomRowBuilder}
|
||||
/// Widget builder for building a bottom row below the message
|
||||
/// {@endtemplate}
|
||||
final Widget Function(BuildContext, Message)? bottomRowBuilder;
|
||||
final BottomRowBuilder? bottomRowBuilder;
|
||||
|
||||
/// {@template bottomRowBuilderWithDefaultWidget}
|
||||
/// Widget builder for building a bottom row below the message.
|
||||
/// Also contains the default bottom row widget.
|
||||
/// {@endtemplate}
|
||||
final BottomRowBuilderWithDefaultWidget? bottomRowBuilderWithDefaultWidget;
|
||||
|
||||
/// {@template deletedBottomRowBuilder}
|
||||
/// Widget builder for building a bottom row below a deleted message
|
||||
@@ -512,6 +539,9 @@ class StreamMessageWidget extends StatefulWidget {
|
||||
/// {@macro onMessageWidgetAttachmentTap}
|
||||
final OnMessageWidgetAttachmentTap? onAttachmentTap;
|
||||
|
||||
/// {@macro attachmentActionsBuilder}
|
||||
final AttachmentActionsBuilder? attachmentActionsModalBuilder;
|
||||
|
||||
/// Size of the image attachment thumbnail.
|
||||
final Size imageAttachmentThumbnailSize;
|
||||
|
||||
@@ -537,9 +567,19 @@ class StreamMessageWidget extends StatefulWidget {
|
||||
void Function(Message)? onReplyTap,
|
||||
Widget Function(BuildContext, Message)? editMessageInputBuilder,
|
||||
Widget Function(BuildContext, Message)? textBuilder,
|
||||
Widget Function(BuildContext, Message)? usernameBuilder,
|
||||
Widget Function(BuildContext, Message)? bottomRowBuilder,
|
||||
Widget Function(BuildContext, Message)? deletedBottomRowBuilder,
|
||||
@Deprecated('''
|
||||
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||
Will be removed in the next major version.
|
||||
''') Widget Function(BuildContext, Message)? usernameBuilder,
|
||||
@Deprecated('''
|
||||
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||
Will be removed in the next major version.
|
||||
''') BottomRowBuilder? bottomRowBuilder,
|
||||
BottomRowBuilderWithDefaultWidget? bottomRowBuilderWithDefaultWidget,
|
||||
@Deprecated('''
|
||||
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||
Will be removed in the next major version.
|
||||
''') Widget Function(BuildContext, Message)? deletedBottomRowBuilder,
|
||||
void Function(BuildContext, Message)? onMessageActions,
|
||||
Message? message,
|
||||
StreamMessageThemeData? messageTheme,
|
||||
@@ -586,7 +626,31 @@ class StreamMessageWidget extends StatefulWidget {
|
||||
Size? imageAttachmentThumbnailSize,
|
||||
String? imageAttachmentThumbnailResizeType,
|
||||
String? imageAttachmentThumbnailCropType,
|
||||
AttachmentActionsBuilder? attachmentActionsModalBuilder,
|
||||
}) {
|
||||
assert(
|
||||
bottomRowBuilder == null || bottomRowBuilderWithDefaultWidget == null,
|
||||
'You can only use one of the two bottom row builders',
|
||||
);
|
||||
|
||||
var _bottomRowBuilderWithDefaultWidget =
|
||||
bottomRowBuilderWithDefaultWidget ??
|
||||
this.bottomRowBuilderWithDefaultWidget;
|
||||
|
||||
_bottomRowBuilderWithDefaultWidget ??= (context, message, defaultWidget) {
|
||||
final _bottomRowBuilder = bottomRowBuilder ?? this.bottomRowBuilder;
|
||||
if (_bottomRowBuilder != null) {
|
||||
return _bottomRowBuilder(context, message);
|
||||
}
|
||||
|
||||
return defaultWidget.copyWith(
|
||||
onThreadTap: onThreadTap ?? this.onThreadTap,
|
||||
usernameBuilder: usernameBuilder ?? this.usernameBuilder,
|
||||
deletedBottomRowBuilder:
|
||||
deletedBottomRowBuilder ?? this.deletedBottomRowBuilder,
|
||||
);
|
||||
};
|
||||
|
||||
return StreamMessageWidget(
|
||||
key: key ?? this.key,
|
||||
onMentionTap: onMentionTap ?? this.onMentionTap,
|
||||
@@ -595,10 +659,7 @@ class StreamMessageWidget extends StatefulWidget {
|
||||
editMessageInputBuilder:
|
||||
editMessageInputBuilder ?? this.editMessageInputBuilder,
|
||||
textBuilder: textBuilder ?? this.textBuilder,
|
||||
usernameBuilder: usernameBuilder ?? this.usernameBuilder,
|
||||
bottomRowBuilder: bottomRowBuilder ?? this.bottomRowBuilder,
|
||||
deletedBottomRowBuilder:
|
||||
deletedBottomRowBuilder ?? this.deletedBottomRowBuilder,
|
||||
bottomRowBuilderWithDefaultWidget: _bottomRowBuilderWithDefaultWidget,
|
||||
onMessageActions: onMessageActions ?? this.onMessageActions,
|
||||
message: message ?? this.message,
|
||||
messageTheme: messageTheme ?? this.messageTheme,
|
||||
@@ -652,6 +713,8 @@ class StreamMessageWidget extends StatefulWidget {
|
||||
this.imageAttachmentThumbnailResizeType,
|
||||
imageAttachmentThumbnailCropType: imageAttachmentThumbnailCropType ??
|
||||
this.imageAttachmentThumbnailCropType,
|
||||
attachmentActionsModalBuilder:
|
||||
attachmentActionsModalBuilder ?? this.attachmentActionsModalBuilder,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -838,52 +901,69 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
|
||||
? Alignment.centerRight
|
||||
: Alignment.centerLeft,
|
||||
widthFactor: widget.widthFactor,
|
||||
child: MessageWidgetContent(
|
||||
streamChatTheme: _streamChatTheme,
|
||||
showUsername: showUsername,
|
||||
showTimeStamp: showTimeStamp,
|
||||
showThreadReplyIndicator: showThreadReplyIndicator,
|
||||
showSendingIndicator: showSendingIndicator,
|
||||
showInChannel: showInChannel,
|
||||
isGiphy: isGiphy,
|
||||
isOnlyEmoji: isOnlyEmoji,
|
||||
hasUrlAttachments: hasUrlAttachments,
|
||||
messageTheme: widget.messageTheme,
|
||||
reverse: widget.reverse,
|
||||
message: widget.message,
|
||||
hasNonUrlAttachments: hasNonUrlAttachments,
|
||||
shouldShowReactions: shouldShowReactions,
|
||||
hasQuotedMessage: hasQuotedMessage,
|
||||
textPadding: widget.textPadding,
|
||||
attachmentBuilders: widget.attachmentBuilders,
|
||||
attachmentPadding: widget.attachmentPadding,
|
||||
avatarWidth: avatarWidth,
|
||||
bottomRowPadding: bottomRowPadding,
|
||||
isFailedState: isFailedState,
|
||||
isPinned: isPinned,
|
||||
messageWidget: widget,
|
||||
showBottomRow: showBottomRow,
|
||||
showPinHighlight: widget.showPinHighlight,
|
||||
showReactionPickerIndicator:
|
||||
widget.showReactionPickerIndicator,
|
||||
showReactions: showReactions,
|
||||
showUserAvatar: widget.showUserAvatar,
|
||||
streamChat: _streamChat,
|
||||
translateUserAvatar: widget.translateUserAvatar,
|
||||
deletedBottomRowBuilder: widget.deletedBottomRowBuilder,
|
||||
onThreadTap: widget.onThreadTap,
|
||||
shape: widget.shape,
|
||||
borderSide: widget.borderSide,
|
||||
borderRadiusGeometry: widget.borderRadiusGeometry,
|
||||
textBuilder: widget.textBuilder,
|
||||
onLinkTap: widget.onLinkTap,
|
||||
onMentionTap: widget.onMentionTap,
|
||||
onQuotedMessageTap: widget.onQuotedMessageTap,
|
||||
bottomRowBuilder: widget.bottomRowBuilder,
|
||||
onUserAvatarTap: widget.onUserAvatarTap,
|
||||
userAvatarBuilder: widget.userAvatarBuilder,
|
||||
usernameBuilder: widget.usernameBuilder,
|
||||
),
|
||||
child: Builder(builder: (context) {
|
||||
var _bottomRowBuilderWithDefaultWidget =
|
||||
widget.bottomRowBuilderWithDefaultWidget;
|
||||
|
||||
_bottomRowBuilderWithDefaultWidget ??=
|
||||
(context, message, defaultWidget) {
|
||||
final _bottomRowBuilder = widget.bottomRowBuilder;
|
||||
if (_bottomRowBuilder != null) {
|
||||
return _bottomRowBuilder(context, message);
|
||||
}
|
||||
|
||||
return defaultWidget.copyWith(
|
||||
onThreadTap: widget.onThreadTap,
|
||||
usernameBuilder: widget.usernameBuilder,
|
||||
deletedBottomRowBuilder: widget.deletedBottomRowBuilder,
|
||||
);
|
||||
};
|
||||
|
||||
return MessageWidgetContent(
|
||||
streamChatTheme: _streamChatTheme,
|
||||
showUsername: showUsername,
|
||||
showTimeStamp: showTimeStamp,
|
||||
showThreadReplyIndicator: showThreadReplyIndicator,
|
||||
showSendingIndicator: showSendingIndicator,
|
||||
showInChannel: showInChannel,
|
||||
isGiphy: isGiphy,
|
||||
isOnlyEmoji: isOnlyEmoji,
|
||||
hasUrlAttachments: hasUrlAttachments,
|
||||
messageTheme: widget.messageTheme,
|
||||
reverse: widget.reverse,
|
||||
message: widget.message,
|
||||
hasNonUrlAttachments: hasNonUrlAttachments,
|
||||
shouldShowReactions: shouldShowReactions,
|
||||
hasQuotedMessage: hasQuotedMessage,
|
||||
textPadding: widget.textPadding,
|
||||
attachmentBuilders: widget.attachmentBuilders,
|
||||
attachmentPadding: widget.attachmentPadding,
|
||||
avatarWidth: avatarWidth,
|
||||
bottomRowPadding: bottomRowPadding,
|
||||
isFailedState: isFailedState,
|
||||
isPinned: isPinned,
|
||||
messageWidget: widget,
|
||||
showBottomRow: showBottomRow,
|
||||
showPinHighlight: widget.showPinHighlight,
|
||||
showReactionPickerIndicator:
|
||||
widget.showReactionPickerIndicator,
|
||||
showReactions: showReactions,
|
||||
showUserAvatar: widget.showUserAvatar,
|
||||
streamChat: _streamChat,
|
||||
translateUserAvatar: widget.translateUserAvatar,
|
||||
shape: widget.shape,
|
||||
borderSide: widget.borderSide,
|
||||
borderRadiusGeometry: widget.borderRadiusGeometry,
|
||||
textBuilder: widget.textBuilder,
|
||||
onLinkTap: widget.onLinkTap,
|
||||
onMentionTap: widget.onMentionTap,
|
||||
onQuotedMessageTap: widget.onQuotedMessageTap,
|
||||
bottomRowBuilderWithDefaultWidget:
|
||||
_bottomRowBuilderWithDefaultWidget,
|
||||
onUserAvatarTap: widget.onUserAvatarTap,
|
||||
userAvatarBuilder: widget.userAvatarBuilder,
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -4,6 +4,18 @@ import 'package:stream_chat_flutter/src/message_widget/message_widget_content_co
|
||||
import 'package:stream_chat_flutter/src/message_widget/reactions/desktop_reactions_builder.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
/// Signature for the builder function that will be called when the message
|
||||
/// bottom row is built. Includes the [Message].
|
||||
typedef BottomRowBuilder = Widget Function(BuildContext, Message);
|
||||
|
||||
/// Signature for the builder function that will be called when the message
|
||||
/// bottom row is built. Includes the [Message] and the default [BottomRow].
|
||||
typedef BottomRowBuilderWithDefaultWidget = Widget Function(
|
||||
BuildContext,
|
||||
Message,
|
||||
BottomRow,
|
||||
);
|
||||
|
||||
/// {@template messageWidgetContent}
|
||||
/// The main content of a [StreamMessageWidget].
|
||||
///
|
||||
@@ -51,12 +63,28 @@ class MessageWidgetContent extends StatelessWidget {
|
||||
this.onMentionTap,
|
||||
this.onLinkTap,
|
||||
this.textBuilder,
|
||||
this.bottomRowBuilder,
|
||||
this.onThreadTap,
|
||||
this.deletedBottomRowBuilder,
|
||||
@Deprecated('''
|
||||
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||
Will be removed in the next major version.
|
||||
''') this.bottomRowBuilder,
|
||||
this.bottomRowBuilderWithDefaultWidget,
|
||||
@Deprecated('''
|
||||
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||
Will be removed in the next major version.
|
||||
''') this.onThreadTap,
|
||||
@Deprecated('''
|
||||
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||
Will be removed in the next major version.
|
||||
''') this.deletedBottomRowBuilder,
|
||||
this.userAvatarBuilder,
|
||||
this.usernameBuilder,
|
||||
});
|
||||
@Deprecated('''
|
||||
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||
Will be removed in the next major version.
|
||||
''') this.usernameBuilder,
|
||||
}) : assert(
|
||||
bottomRowBuilder == null || bottomRowBuilderWithDefaultWidget == null,
|
||||
'You can only use one of the two bottom row builders',
|
||||
);
|
||||
|
||||
/// {@macro reverse}
|
||||
final bool reverse;
|
||||
@@ -152,7 +180,10 @@ class MessageWidgetContent extends StatelessWidget {
|
||||
final double bottomRowPadding;
|
||||
|
||||
/// {@macro bottomRowBuilder}
|
||||
final Widget Function(BuildContext, Message)? bottomRowBuilder;
|
||||
final BottomRowBuilder? bottomRowBuilder;
|
||||
|
||||
/// {@macro bottomRowBuilderWithDefaultWidget}
|
||||
final BottomRowBuilderWithDefaultWidget? bottomRowBuilderWithDefaultWidget;
|
||||
|
||||
/// {@macro showInChannelIndicator}
|
||||
final bool showInChannel;
|
||||
@@ -207,30 +238,7 @@ class MessageWidgetContent extends StatelessWidget {
|
||||
right: reverse ? bottomRowPadding : 0,
|
||||
bottom: isPinned && showPinHighlight ? 6.0 : 0.0,
|
||||
),
|
||||
child: bottomRowBuilder?.call(
|
||||
context,
|
||||
message,
|
||||
) ??
|
||||
BottomRow(
|
||||
message: message,
|
||||
reverse: reverse,
|
||||
messageTheme: messageTheme,
|
||||
hasUrlAttachments: hasUrlAttachments,
|
||||
isOnlyEmoji: isOnlyEmoji,
|
||||
isDeleted: message.isDeleted,
|
||||
isGiphy: isGiphy,
|
||||
showInChannel: showInChannel,
|
||||
showSendingIndicator: showSendingIndicator,
|
||||
showThreadReplyIndicator: showThreadReplyIndicator,
|
||||
showTimeStamp: showTimeStamp,
|
||||
showUsername: showUsername,
|
||||
streamChatTheme: streamChatTheme,
|
||||
onThreadTap: onThreadTap,
|
||||
deletedBottomRowBuilder: deletedBottomRowBuilder,
|
||||
streamChat: streamChat,
|
||||
hasNonUrlAttachments: hasNonUrlAttachments,
|
||||
usernameBuilder: usernameBuilder,
|
||||
),
|
||||
child: _buildBottomRow(context),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
@@ -457,4 +465,39 @@ class MessageWidgetContent extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBottomRow(BuildContext context) {
|
||||
final defaultWidget = BottomRow(
|
||||
message: message,
|
||||
reverse: reverse,
|
||||
messageTheme: messageTheme,
|
||||
hasUrlAttachments: hasUrlAttachments,
|
||||
isOnlyEmoji: isOnlyEmoji,
|
||||
isDeleted: message.isDeleted,
|
||||
isGiphy: isGiphy,
|
||||
showInChannel: showInChannel,
|
||||
showSendingIndicator: showSendingIndicator,
|
||||
showThreadReplyIndicator: showThreadReplyIndicator,
|
||||
showTimeStamp: showTimeStamp,
|
||||
showUsername: showUsername,
|
||||
streamChatTheme: streamChatTheme,
|
||||
onThreadTap: onThreadTap,
|
||||
deletedBottomRowBuilder: deletedBottomRowBuilder,
|
||||
streamChat: streamChat,
|
||||
hasNonUrlAttachments: hasNonUrlAttachments,
|
||||
usernameBuilder: usernameBuilder,
|
||||
);
|
||||
|
||||
if (bottomRowBuilder != null) {
|
||||
return bottomRowBuilder!(context, message);
|
||||
} else if (bottomRowBuilderWithDefaultWidget != null) {
|
||||
return bottomRowBuilderWithDefaultWidget!(
|
||||
context,
|
||||
message,
|
||||
defaultWidget,
|
||||
);
|
||||
}
|
||||
|
||||
return defaultWidget;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1088,7 +1088,12 @@ class StreamSvgIcon extends StatelessWidget {
|
||||
key: key,
|
||||
width: width,
|
||||
height: height,
|
||||
color: color,
|
||||
colorFilter: color != null
|
||||
? ColorFilter.mode(
|
||||
color!,
|
||||
BlendMode.srcIn,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,8 +149,8 @@ class StreamThreadHeader extends StatelessWidget
|
||||
final theme = Theme.of(context);
|
||||
return AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
toolbarTextStyle: theme.textTheme.bodyText2,
|
||||
titleTextStyle: theme.textTheme.headline6,
|
||||
toolbarTextStyle: theme.textTheme.bodyMedium,
|
||||
titleTextStyle: theme.textTheme.titleLarge,
|
||||
systemOverlayStyle: theme.brightness == Brightness.dark
|
||||
? SystemUiOverlayStyle.light
|
||||
: SystemUiOverlayStyle.dark,
|
||||
|
||||
+1
-1
@@ -409,7 +409,7 @@ class StreamChannelListErrorWidget extends StatelessWidget {
|
||||
TextSpan(text: context.translations.loadingChannelsError),
|
||||
],
|
||||
),
|
||||
style: Theme.of(context).textTheme.headline6,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
TextButton(
|
||||
onPressed: onPressed,
|
||||
|
||||
+3
-3
@@ -366,7 +366,7 @@ class StreamPhotoGallery extends StatelessWidget {
|
||||
color: chatThemeData.colorTheme.disabled,
|
||||
),
|
||||
emptyTitle: Text(
|
||||
context.translations.noUsersLabel,
|
||||
context.translations.noPhotoOrVideoLabel,
|
||||
style: chatThemeData.textTheme.headline,
|
||||
),
|
||||
),
|
||||
@@ -377,7 +377,7 @@ class StreamPhotoGallery extends StatelessWidget {
|
||||
return StreamScrollViewLoadMoreError.grid(
|
||||
onTap: controller.retry,
|
||||
error: Text(
|
||||
context.translations.loadingUsersError,
|
||||
context.translations.genericErrorText,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
@@ -400,7 +400,7 @@ class StreamPhotoGallery extends StatelessWidget {
|
||||
return errorBuilder?.call(context, error) ??
|
||||
Center(
|
||||
child: StreamScrollViewErrorWidget(
|
||||
errorTitle: Text(context.translations.loadingUsersError),
|
||||
errorTitle: Text(context.translations.genericErrorText),
|
||||
onRetryPressed: controller.refresh,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -21,6 +21,7 @@ class StreamMessageThemeData with Diagnosticable {
|
||||
this.avatarTheme,
|
||||
this.createdAtStyle,
|
||||
this.linkBackgroundColor,
|
||||
this.urlAttachmentTitleMaxLine,
|
||||
});
|
||||
|
||||
/// Text style for message text
|
||||
@@ -59,6 +60,9 @@ class StreamMessageThemeData with Diagnosticable {
|
||||
/// Background color for messages with url attachments.
|
||||
final Color? linkBackgroundColor;
|
||||
|
||||
/// Max number of lines in Url link title
|
||||
final int? urlAttachmentTitleMaxLine;
|
||||
|
||||
/// Copy with a theme
|
||||
StreamMessageThemeData copyWith({
|
||||
TextStyle? messageTextStyle,
|
||||
@@ -73,6 +77,7 @@ class StreamMessageThemeData with Diagnosticable {
|
||||
Color? reactionsBorderColor,
|
||||
Color? reactionsMaskColor,
|
||||
Color? linkBackgroundColor,
|
||||
int? urlAttachmentTitleMaxLine,
|
||||
}) {
|
||||
return StreamMessageThemeData(
|
||||
messageTextStyle: messageTextStyle ?? this.messageTextStyle,
|
||||
@@ -89,6 +94,8 @@ class StreamMessageThemeData with Diagnosticable {
|
||||
reactionsBorderColor: reactionsBorderColor ?? this.reactionsBorderColor,
|
||||
reactionsMaskColor: reactionsMaskColor ?? this.reactionsMaskColor,
|
||||
linkBackgroundColor: linkBackgroundColor ?? this.linkBackgroundColor,
|
||||
urlAttachmentTitleMaxLine:
|
||||
urlAttachmentTitleMaxLine ?? this.urlAttachmentTitleMaxLine,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -148,6 +155,7 @@ class StreamMessageThemeData with Diagnosticable {
|
||||
reactionsBorderColor: other.reactionsBorderColor,
|
||||
reactionsMaskColor: other.reactionsMaskColor,
|
||||
linkBackgroundColor: other.linkBackgroundColor,
|
||||
urlAttachmentTitleMaxLine: other.urlAttachmentTitleMaxLine,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -167,7 +175,8 @@ class StreamMessageThemeData with Diagnosticable {
|
||||
reactionsBorderColor == other.reactionsBorderColor &&
|
||||
reactionsMaskColor == other.reactionsMaskColor &&
|
||||
avatarTheme == other.avatarTheme &&
|
||||
linkBackgroundColor == other.linkBackgroundColor;
|
||||
linkBackgroundColor == other.linkBackgroundColor &&
|
||||
urlAttachmentTitleMaxLine == other.urlAttachmentTitleMaxLine;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
@@ -182,7 +191,8 @@ class StreamMessageThemeData with Diagnosticable {
|
||||
reactionsBorderColor.hashCode ^
|
||||
reactionsMaskColor.hashCode ^
|
||||
avatarTheme.hashCode ^
|
||||
linkBackgroundColor.hashCode;
|
||||
linkBackgroundColor.hashCode ^
|
||||
urlAttachmentTitleMaxLine.hashCode;
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
@@ -199,6 +209,10 @@ class StreamMessageThemeData with Diagnosticable {
|
||||
..add(ColorProperty('reactionsBackgroundColor', reactionsBackgroundColor))
|
||||
..add(ColorProperty('reactionsBorderColor', reactionsBorderColor))
|
||||
..add(ColorProperty('reactionsMaskColor', reactionsMaskColor))
|
||||
..add(ColorProperty('linkBackgroundColor', linkBackgroundColor));
|
||||
..add(ColorProperty('linkBackgroundColor', linkBackgroundColor))
|
||||
..add(DiagnosticsProperty(
|
||||
'urlAttachmentTitleMaxLine',
|
||||
urlAttachmentTitleMaxLine,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ class _IVideoService {
|
||||
///
|
||||
/// Thumbnails are not supported on Web at this time.
|
||||
///
|
||||
/// If no [video] path is supplied, or if a thumbnail cannot be generated,
|
||||
/// returns [generatePlaceholderThumbnail]. A stock placeholder image.
|
||||
///
|
||||
/// For desktop, you can specify the position of the video to generate
|
||||
/// the thumbnail.
|
||||
///
|
||||
@@ -29,14 +32,14 @@ class _IVideoService {
|
||||
/// creates lower quality of the thumbnail image, but it gets ignored for
|
||||
/// PNG format.
|
||||
Future<Uint8List?> generateVideoThumbnail({
|
||||
required String video,
|
||||
String? video,
|
||||
ImageFormat imageFormat = ImageFormat.PNG,
|
||||
int maxHeight = 0,
|
||||
int maxWidth = 0,
|
||||
int timeMs = 0,
|
||||
int quality = 10,
|
||||
}) async {
|
||||
if (kIsWeb) {
|
||||
if (kIsWeb || video == null) {
|
||||
final placeholder = await generatePlaceholderThumbnail();
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
@@ -8,13 +8,23 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:video_thumbnail/video_thumbnail.dart';
|
||||
|
||||
/// {@template streamVideoThumbnailImage}
|
||||
/// Displays a video thumbnail for video attachments in a message.
|
||||
/// Displays a video thumbnail for video attachments.
|
||||
///
|
||||
/// [thumbUrl] is used if provided.
|
||||
///
|
||||
/// Else [video] (path to local or remote video) is used to generate
|
||||
/// a thumbnail from the video asset.
|
||||
///
|
||||
/// WARNING! a local path does not work on web.
|
||||
///
|
||||
/// If both [thumbUrl] and [video] are null, or if a thumbnail can't be
|
||||
/// generated, a stock default image will be used.
|
||||
/// {@endtemplate}
|
||||
class StreamVideoThumbnailImage extends StatefulWidget {
|
||||
/// {@macro streamVideoThumbnailImage}
|
||||
const StreamVideoThumbnailImage({
|
||||
super.key,
|
||||
required this.video,
|
||||
this.video,
|
||||
this.thumbUrl,
|
||||
this.constraints,
|
||||
this.fit = BoxFit.cover,
|
||||
@@ -24,7 +34,7 @@ class StreamVideoThumbnailImage extends StatefulWidget {
|
||||
});
|
||||
|
||||
/// Video path or url
|
||||
final String video;
|
||||
final String? video;
|
||||
|
||||
/// Video thumbnail url
|
||||
final String? thumbUrl;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:stream_chat_flutter/src/video/vlc/vlc_stub.dart'
|
||||
if (dart.library.io) 'vlc_manager_desktop.dart'
|
||||
if (dar.library.html) 'vlc_manager_web.dart';
|
||||
if (dart.library.html) 'vlc_manager_web.dart';
|
||||
|
||||
/// {@template vlcManager}
|
||||
/// An abstract class for the purpose of ensuring Flutter applications that
|
||||
|
||||
@@ -83,8 +83,6 @@ export 'src/scroll_view/photo_gallery/stream_photo_gallery_tile.dart';
|
||||
export 'src/scroll_view/stream_scroll_view_empty_widget.dart';
|
||||
export 'src/scroll_view/stream_scroll_view_indexed_widget_builder.dart';
|
||||
export 'src/scroll_view/user_scroll_view/stream_user_grid_tile.dart';
|
||||
export 'src/scroll_view/user_scroll_view/stream_user_grid_tile.dart';
|
||||
export 'src/scroll_view/user_scroll_view/stream_user_grid_view.dart';
|
||||
export 'src/scroll_view/user_scroll_view/stream_user_grid_view.dart';
|
||||
export 'src/scroll_view/user_scroll_view/stream_user_list_tile.dart';
|
||||
export 'src/scroll_view/user_scroll_view/stream_user_list_view.dart';
|
||||
|
||||
@@ -1,50 +1,48 @@
|
||||
name: stream_chat_flutter
|
||||
homepage: https://github.com/GetStream/stream-chat-flutter
|
||||
description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter.
|
||||
version: 5.1.0
|
||||
version: 5.3.0
|
||||
repository: https://github.com/GetStream/stream-chat-flutter
|
||||
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
||||
|
||||
environment:
|
||||
sdk: '>=2.17.0 <3.0.0'
|
||||
sdk: ">=2.17.0 <3.0.0"
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
dependencies:
|
||||
cached_network_image: ^3.0.0
|
||||
chewie: ^1.3.4
|
||||
collection: ^1.15.0
|
||||
collection: ^1.17.0
|
||||
contextmenu: ^3.0.0
|
||||
dart_vlc: ^0.3.0
|
||||
dart_vlc: ^0.4.0
|
||||
desktop_drop: ^0.4.0
|
||||
diacritic: ^0.1.3
|
||||
dio: ^4.0.6
|
||||
ezanimation: ^0.6.0
|
||||
file_picker: ^4.1.3
|
||||
file_picker: ^5.2.4
|
||||
file_selector: ^0.9.0
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_markdown: ^0.6.1
|
||||
flutter_portal: ^1.0.0
|
||||
flutter_svg: ^1.0.1
|
||||
http: ^0.13.4
|
||||
flutter_svg: ^2.0.4
|
||||
http_parser: ^4.0.0
|
||||
image_gallery_saver: ^1.7.0
|
||||
image_gallery_saver: ^1.7.1
|
||||
image_picker: ^0.8.2
|
||||
jiffy: ^5.0.0
|
||||
lottie: ^2.0.0
|
||||
meta: ^1.3.0
|
||||
meta: ^1.8.0
|
||||
path_provider: ^2.0.9
|
||||
photo_manager: ^2.0.1
|
||||
photo_manager: ^2.5.2
|
||||
photo_view: ^0.14.0
|
||||
rxdart: ^0.27.0
|
||||
share_plus: ^4.5.0
|
||||
share_plus: ^6.3.0
|
||||
shimmer: ^2.0.0
|
||||
stream_chat_flutter_core: ^5.1.0
|
||||
stream_chat_flutter_core: ^5.3.0
|
||||
synchronized: ^3.0.0
|
||||
thumblr: ^0.0.4
|
||||
url_launcher: ^6.1.0
|
||||
video_player: ^2.4.5
|
||||
video_player_macos: ^1.0.6
|
||||
video_player_macos: ^2.0.1
|
||||
video_thumbnail: ^0.5.0
|
||||
|
||||
flutter:
|
||||
@@ -56,9 +54,9 @@ flutter:
|
||||
uses-material-design: true
|
||||
|
||||
dev_dependencies:
|
||||
dart_code_metrics: ^4.4.0
|
||||
dart_code_metrics: ^5.7.0
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
golden_toolkit: ^0.13.0
|
||||
golden_toolkit: ^0.15.0
|
||||
mocktail: ^0.3.0
|
||||
path: ^1.8.0
|
||||
path: ^1.8.2
|
||||
|
||||
+4
-4
@@ -163,7 +163,7 @@ void main() {
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on reply should pop',
|
||||
'tapping on reply should invoke callback',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
@@ -174,7 +174,7 @@ void main() {
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||
|
||||
final mockObserver = MockNavigatorObserver();
|
||||
final mockCallback = MockVoidCallback();
|
||||
|
||||
final attachment = Attachment(
|
||||
type: 'image',
|
||||
@@ -192,7 +192,6 @@ void main() {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
navigatorObservers: [mockObserver],
|
||||
home: StreamChat(
|
||||
streamChatThemeData: streamTheme,
|
||||
client: client,
|
||||
@@ -200,13 +199,14 @@ void main() {
|
||||
child: AttachmentActionsModal(
|
||||
message: message,
|
||||
attachment: attachment,
|
||||
onReply: mockCallback,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.tap(find.text('Reply'));
|
||||
verify(() => mockObserver.didPop(any(), any()));
|
||||
verify(mockCallback.call);
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
## Upcomming
|
||||
## Upcoming
|
||||
|
||||
- Updated dependencies to resolvable versions.
|
||||
|
||||
## 5.3.0
|
||||
|
||||
- Updated `stream_chat` dependency to [`5.3.0`](https://pub.dev/packages/stream_chat/changelog).
|
||||
|
||||
## 5.2.0
|
||||
|
||||
🔄 Changed
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ apply plugin: 'com.android.application'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 33
|
||||
|
||||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
@@ -35,7 +35,7 @@ android {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "example.example"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 33
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
@@ -7,14 +7,15 @@
|
||||
FlutterApplication and put your custom class here. -->
|
||||
<application
|
||||
android:name="${applicationName}"
|
||||
android:label="example"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="example">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:exported="true"
|
||||
android:hardwareAccelerated="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.7.21'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
||||
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
|
||||
|
||||
@@ -19,6 +19,7 @@ version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: '>=2.17.0 <3.0.0'
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
dependencies:
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
@@ -27,6 +28,7 @@ dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
stream_chat_flutter_core: ^5.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <connectivity_plus_windows/connectivity_plus_windows_plugin.h>
|
||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
connectivity_plus_windows
|
||||
connectivity_plus
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
@@ -83,7 +83,7 @@ abstract class PagedValueNotifier<Key, Value>
|
||||
|
||||
/// Paged value that can be used with [PagedValueNotifier].
|
||||
@freezed
|
||||
abstract class PagedValue<Key, Value> with _$PagedValue<Key, Value> {
|
||||
class PagedValue<Key, Value> with _$PagedValue<Key, Value> {
|
||||
/// Represents the success state of the [PagedValue]
|
||||
// @Assert(
|
||||
// 'nextPageKey != null',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'paged_value_notifier.dart';
|
||||
|
||||
@@ -27,11 +27,11 @@ mixin _$PagedValue<Key, Value> {
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult Function(
|
||||
TResult? Function(
|
||||
List<Value> items, Key? nextPageKey, StreamChatError? error)?
|
||||
$default, {
|
||||
TResult Function()? loading,
|
||||
TResult Function(StreamChatError error)? error,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(StreamChatError error)? error,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
@@ -53,9 +53,9 @@ mixin _$PagedValue<Key, Value> {
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>(
|
||||
TResult Function(Success<Key, Value> value)? $default, {
|
||||
TResult Function(Loading<Key, Value> value)? loading,
|
||||
TResult Function(Error<Key, Value> value)? error,
|
||||
TResult? Function(Success<Key, Value> value)? $default, {
|
||||
TResult? Function(Loading<Key, Value> value)? loading,
|
||||
TResult? Function(Error<Key, Value> value)? error,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
@@ -72,17 +72,19 @@ mixin _$PagedValue<Key, Value> {
|
||||
abstract class $PagedValueCopyWith<Key, Value, $Res> {
|
||||
factory $PagedValueCopyWith(PagedValue<Key, Value> value,
|
||||
$Res Function(PagedValue<Key, Value>) then) =
|
||||
_$PagedValueCopyWithImpl<Key, Value, $Res>;
|
||||
_$PagedValueCopyWithImpl<Key, Value, $Res, PagedValue<Key, Value>>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$PagedValueCopyWithImpl<Key, Value, $Res>
|
||||
class _$PagedValueCopyWithImpl<Key, Value, $Res,
|
||||
$Val extends PagedValue<Key, Value>>
|
||||
implements $PagedValueCopyWith<Key, Value, $Res> {
|
||||
_$PagedValueCopyWithImpl(this._value, this._then);
|
||||
|
||||
final PagedValue<Key, Value> _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function(PagedValue<Key, Value>) _then;
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -90,36 +92,35 @@ abstract class _$$SuccessCopyWith<Key, Value, $Res> {
|
||||
factory _$$SuccessCopyWith(_$Success<Key, Value> value,
|
||||
$Res Function(_$Success<Key, Value>) then) =
|
||||
__$$SuccessCopyWithImpl<Key, Value, $Res>;
|
||||
@useResult
|
||||
$Res call({List<Value> items, Key? nextPageKey, StreamChatError? error});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$SuccessCopyWithImpl<Key, Value, $Res>
|
||||
extends _$PagedValueCopyWithImpl<Key, Value, $Res>
|
||||
extends _$PagedValueCopyWithImpl<Key, Value, $Res, _$Success<Key, Value>>
|
||||
implements _$$SuccessCopyWith<Key, Value, $Res> {
|
||||
__$$SuccessCopyWithImpl(
|
||||
_$Success<Key, Value> _value, $Res Function(_$Success<Key, Value>) _then)
|
||||
: super(_value, (v) => _then(v as _$Success<Key, Value>));
|
||||
|
||||
@override
|
||||
_$Success<Key, Value> get _value => super._value as _$Success<Key, Value>;
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? items = freezed,
|
||||
Object? items = null,
|
||||
Object? nextPageKey = freezed,
|
||||
Object? error = freezed,
|
||||
}) {
|
||||
return _then(_$Success<Key, Value>(
|
||||
items: items == freezed
|
||||
items: null == items
|
||||
? _value._items
|
||||
: items // ignore: cast_nullable_to_non_nullable
|
||||
as List<Value>,
|
||||
nextPageKey: nextPageKey == freezed
|
||||
nextPageKey: freezed == nextPageKey
|
||||
? _value.nextPageKey
|
||||
: nextPageKey // ignore: cast_nullable_to_non_nullable
|
||||
as Key?,
|
||||
error: error == freezed
|
||||
error: freezed == error
|
||||
? _value.error
|
||||
: error // ignore: cast_nullable_to_non_nullable
|
||||
as StreamChatError?,
|
||||
@@ -142,6 +143,7 @@ class _$Success<Key, Value> extends Success<Key, Value>
|
||||
/// List with all items loaded so far.
|
||||
@override
|
||||
List<Value> get items {
|
||||
if (_items is EqualUnmodifiableListView) return _items;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_items);
|
||||
}
|
||||
@@ -177,7 +179,7 @@ class _$Success<Key, Value> extends Success<Key, Value>
|
||||
const DeepCollectionEquality().equals(other._items, _items) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.nextPageKey, nextPageKey) &&
|
||||
const DeepCollectionEquality().equals(other.error, error));
|
||||
(identical(other.error, error) || other.error == error));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -185,10 +187,11 @@ class _$Success<Key, Value> extends Success<Key, Value>
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_items),
|
||||
const DeepCollectionEquality().hash(nextPageKey),
|
||||
const DeepCollectionEquality().hash(error));
|
||||
error);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$SuccessCopyWith<Key, Value, _$Success<Key, Value>> get copyWith =>
|
||||
__$$SuccessCopyWithImpl<Key, Value, _$Success<Key, Value>>(
|
||||
this, _$identity);
|
||||
@@ -208,11 +211,11 @@ class _$Success<Key, Value> extends Success<Key, Value>
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult Function(
|
||||
TResult? Function(
|
||||
List<Value> items, Key? nextPageKey, StreamChatError? error)?
|
||||
$default, {
|
||||
TResult Function()? loading,
|
||||
TResult Function(StreamChatError error)? error,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(StreamChatError error)? error,
|
||||
}) {
|
||||
return $default?.call(items, nextPageKey, this.error);
|
||||
}
|
||||
@@ -246,9 +249,9 @@ class _$Success<Key, Value> extends Success<Key, Value>
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>(
|
||||
TResult Function(Success<Key, Value> value)? $default, {
|
||||
TResult Function(Loading<Key, Value> value)? loading,
|
||||
TResult Function(Error<Key, Value> value)? error,
|
||||
TResult? Function(Success<Key, Value> value)? $default, {
|
||||
TResult? Function(Loading<Key, Value> value)? loading,
|
||||
TResult? Function(Error<Key, Value> value)? error,
|
||||
}) {
|
||||
return $default?.call(this);
|
||||
}
|
||||
@@ -276,13 +279,13 @@ abstract class Success<Key, Value> extends PagedValue<Key, Value> {
|
||||
const Success._() : super._();
|
||||
|
||||
/// List with all items loaded so far.
|
||||
List<Value> get items => throw _privateConstructorUsedError;
|
||||
List<Value> get items;
|
||||
|
||||
/// The key for the next page to be fetched.
|
||||
Key? get nextPageKey => throw _privateConstructorUsedError;
|
||||
Key? get nextPageKey;
|
||||
|
||||
/// The current error, if any.
|
||||
StreamChatError? get error => throw _privateConstructorUsedError;
|
||||
StreamChatError? get error;
|
||||
@JsonKey(ignore: true)
|
||||
_$$SuccessCopyWith<Key, Value, _$Success<Key, Value>> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
@@ -297,14 +300,11 @@ abstract class _$$LoadingCopyWith<Key, Value, $Res> {
|
||||
|
||||
/// @nodoc
|
||||
class __$$LoadingCopyWithImpl<Key, Value, $Res>
|
||||
extends _$PagedValueCopyWithImpl<Key, Value, $Res>
|
||||
extends _$PagedValueCopyWithImpl<Key, Value, $Res, _$Loading<Key, Value>>
|
||||
implements _$$LoadingCopyWith<Key, Value, $Res> {
|
||||
__$$LoadingCopyWithImpl(
|
||||
_$Loading<Key, Value> _value, $Res Function(_$Loading<Key, Value>) _then)
|
||||
: super(_value, (v) => _then(v as _$Loading<Key, Value>));
|
||||
|
||||
@override
|
||||
_$Loading<Key, Value> get _value => super._value as _$Loading<Key, Value>;
|
||||
: super(_value, _then);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -349,11 +349,11 @@ class _$Loading<Key, Value> extends Loading<Key, Value>
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult Function(
|
||||
TResult? Function(
|
||||
List<Value> items, Key? nextPageKey, StreamChatError? error)?
|
||||
$default, {
|
||||
TResult Function()? loading,
|
||||
TResult Function(StreamChatError error)? error,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(StreamChatError error)? error,
|
||||
}) {
|
||||
return loading?.call();
|
||||
}
|
||||
@@ -387,9 +387,9 @@ class _$Loading<Key, Value> extends Loading<Key, Value>
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>(
|
||||
TResult Function(Success<Key, Value> value)? $default, {
|
||||
TResult Function(Loading<Key, Value> value)? loading,
|
||||
TResult Function(Error<Key, Value> value)? error,
|
||||
TResult? Function(Success<Key, Value> value)? $default, {
|
||||
TResult? Function(Loading<Key, Value> value)? loading,
|
||||
TResult? Function(Error<Key, Value> value)? error,
|
||||
}) {
|
||||
return loading?.call(this);
|
||||
}
|
||||
@@ -419,26 +419,25 @@ abstract class _$$ErrorCopyWith<Key, Value, $Res> {
|
||||
factory _$$ErrorCopyWith(
|
||||
_$Error<Key, Value> value, $Res Function(_$Error<Key, Value>) then) =
|
||||
__$$ErrorCopyWithImpl<Key, Value, $Res>;
|
||||
@useResult
|
||||
$Res call({StreamChatError error});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ErrorCopyWithImpl<Key, Value, $Res>
|
||||
extends _$PagedValueCopyWithImpl<Key, Value, $Res>
|
||||
extends _$PagedValueCopyWithImpl<Key, Value, $Res, _$Error<Key, Value>>
|
||||
implements _$$ErrorCopyWith<Key, Value, $Res> {
|
||||
__$$ErrorCopyWithImpl(
|
||||
_$Error<Key, Value> _value, $Res Function(_$Error<Key, Value>) _then)
|
||||
: super(_value, (v) => _then(v as _$Error<Key, Value>));
|
||||
|
||||
@override
|
||||
_$Error<Key, Value> get _value => super._value as _$Error<Key, Value>;
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? error = freezed,
|
||||
Object? error = null,
|
||||
}) {
|
||||
return _then(_$Error<Key, Value>(
|
||||
error == freezed
|
||||
null == error
|
||||
? _value.error
|
||||
: error // ignore: cast_nullable_to_non_nullable
|
||||
as StreamChatError,
|
||||
@@ -473,15 +472,15 @@ class _$Error<Key, Value> extends Error<Key, Value>
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$Error<Key, Value> &&
|
||||
const DeepCollectionEquality().equals(other.error, error));
|
||||
(identical(other.error, error) || other.error == error));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(error));
|
||||
int get hashCode => Object.hash(runtimeType, error);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ErrorCopyWith<Key, Value, _$Error<Key, Value>> get copyWith =>
|
||||
__$$ErrorCopyWithImpl<Key, Value, _$Error<Key, Value>>(this, _$identity);
|
||||
|
||||
@@ -500,11 +499,11 @@ class _$Error<Key, Value> extends Error<Key, Value>
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult Function(
|
||||
TResult? Function(
|
||||
List<Value> items, Key? nextPageKey, StreamChatError? error)?
|
||||
$default, {
|
||||
TResult Function()? loading,
|
||||
TResult Function(StreamChatError error)? error,
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(StreamChatError error)? error,
|
||||
}) {
|
||||
return error?.call(this.error);
|
||||
}
|
||||
@@ -538,9 +537,9 @@ class _$Error<Key, Value> extends Error<Key, Value>
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>(
|
||||
TResult Function(Success<Key, Value> value)? $default, {
|
||||
TResult Function(Loading<Key, Value> value)? loading,
|
||||
TResult Function(Error<Key, Value> value)? error,
|
||||
TResult? Function(Success<Key, Value> value)? $default, {
|
||||
TResult? Function(Loading<Key, Value> value)? loading,
|
||||
TResult? Function(Error<Key, Value> value)? error,
|
||||
}) {
|
||||
return error?.call(this);
|
||||
}
|
||||
@@ -564,7 +563,7 @@ abstract class Error<Key, Value> extends PagedValue<Key, Value> {
|
||||
const factory Error(final StreamChatError error) = _$Error<Key, Value>;
|
||||
const Error._() : super._();
|
||||
|
||||
StreamChatError get error => throw _privateConstructorUsedError;
|
||||
StreamChatError get error;
|
||||
@JsonKey(ignore: true)
|
||||
_$$ErrorCopyWith<Key, Value, _$Error<Key, Value>> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
@@ -425,8 +425,8 @@ class StreamChannelState extends State<StreamChannel> {
|
||||
var message = snapshot.error.toString();
|
||||
if (snapshot.error is DioError) {
|
||||
final dioError = snapshot.error as DioError?;
|
||||
if (dioError?.type == DioErrorType.response) {
|
||||
message = dioError!.message;
|
||||
if (dioError?.type == DioErrorType.badResponse) {
|
||||
message = dioError!.message ?? 'Bad response';
|
||||
} else {
|
||||
message = 'Check your connection and retry';
|
||||
}
|
||||
|
||||
@@ -244,10 +244,11 @@ class StreamChannelListController extends PagedValueNotifier<int, Channel> {
|
||||
_unsubscribeFromChannelListEvents();
|
||||
}
|
||||
|
||||
_channelEventSubscription =
|
||||
client.on().skip(1) // Skipping the last emitted event.
|
||||
// We only need to handle the latest events.
|
||||
.listen((event) {
|
||||
_channelEventSubscription = client
|
||||
.on()
|
||||
.skip(1) // Skipping the last emitted event.
|
||||
// We only need to handle the latest events.
|
||||
.listen((event) {
|
||||
// Only handle the event if the value is in success state.
|
||||
if (value.isNotSuccess) return;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: stream_chat_flutter_core
|
||||
homepage: https://github.com/GetStream/stream-chat-flutter
|
||||
description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter.
|
||||
version: 5.1.0
|
||||
version: 5.3.0
|
||||
repository: https://github.com/GetStream/stream-chat-flutter
|
||||
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
||||
|
||||
@@ -10,20 +10,21 @@ environment:
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
dependencies:
|
||||
collection: ^1.15.0
|
||||
collection: ^1.17.0
|
||||
connectivity_plus: ^3.0.2
|
||||
flutter:
|
||||
sdk: flutter
|
||||
freezed_annotation: ^2.0.3
|
||||
meta: ^1.3.0
|
||||
meta: ^1.8.0
|
||||
rxdart: ^0.27.0
|
||||
stream_chat: ^5.1.0
|
||||
stream_chat: ^5.3.0
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.0.1
|
||||
dart_code_metrics: ^4.4.0
|
||||
build_runner: ^2.3.3
|
||||
dart_code_metrics: ^5.7.0
|
||||
fake_async: ^1.2.0
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
freezed: ^2.0.3
|
||||
freezed: ^2.3.2
|
||||
mocktail: ^0.3.0
|
||||
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
Matcher isSameChannelAs(Channel targetChannel) =>
|
||||
_IsSameChannelAs(targetChannel: targetChannel);
|
||||
|
||||
class _IsSameChannelAs extends Matcher {
|
||||
const _IsSameChannelAs({
|
||||
required this.targetChannel,
|
||||
});
|
||||
|
||||
final Channel targetChannel;
|
||||
|
||||
@override
|
||||
bool matches(covariant Channel channel, Map matchState) =>
|
||||
channel.cid == targetChannel.cid;
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
description.add('is same channel as $targetChannel');
|
||||
}
|
||||
|
||||
Matcher isSameChannelListAs(List<Channel> targetChannelList) =>
|
||||
_IsSameChannelListAs(targetChannelList: targetChannelList);
|
||||
|
||||
class _IsSameChannelListAs extends Matcher {
|
||||
const _IsSameChannelListAs({
|
||||
required this.targetChannelList,
|
||||
});
|
||||
|
||||
final List<Channel> targetChannelList;
|
||||
|
||||
@override
|
||||
bool matches(covariant List<Channel> channelList, Map matchState) {
|
||||
var matches = true;
|
||||
for (var i = 0; i < channelList.length; i++) {
|
||||
final channel = channelList[i];
|
||||
final targetChannel = targetChannelList[i];
|
||||
matches = isSameChannelAs(targetChannel).matches(channel, matchState);
|
||||
if (!matches) break;
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
description.add('is same channelList as $targetChannelList');
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user