Merge pull request #381 from GetStream/feature/null-safety

feat: migrate to NNBD(non-nullable by default)
This commit is contained in:
Salvatore Giordano
2021-05-03 20:21:32 +02:00
committed by GitHub
275 changed files with 124895 additions and 9615 deletions
@@ -27,7 +27,7 @@ jobs:
./.github/workflows/scripts/install-tools.sh
flutter pub global activate tuneup
- name: 'Bootstrap Workspace'
run: melos bootstrap
run: melos bootstrap --verbose
- name: 'Dart Analyze'
run: |
melos exec -c 3 --ignore="*example*" -- \
@@ -50,7 +50,7 @@ jobs:
run: |
./.github/workflows/scripts/install-tools.sh
- name: 'Bootstrap Workspace'
run: melos bootstrap
run: melos bootstrap --verbose
- name: 'Dart'
run: |
melos exec -c 1 -- \
@@ -72,7 +72,7 @@ jobs:
flutter pub global activate coverage
flutter pub global activate remove_from_coverage
- name: 'Bootstrap Workspace'
run: melos bootstrap
run: melos bootstrap --verbose
- name: 'Dart Test'
run: |
cd packages/stream_chat
+1 -2
View File
@@ -59,5 +59,4 @@ dev_dependencies:
pedantic: 1.9.2
environment:
sdk: ">=2.7.0 <3.0.0"
flutter: ">=1.22.4 <2.0.0"
sdk: ">=2.12.0 <3.0.0"
+5
View File
@@ -1,3 +1,8 @@
## 2.0.0-nullsafety.0
- Migrate this package to null safety
- Added typed filters
## 1.5.3
- fix: `StreamChatClient.connect` returns quicker when you're using the persistence package
@@ -3,7 +3,6 @@ analyzer:
- lib/**/*.g.dart
- lib/**/*.freezed.dart
- example/*
- test/*
linter:
rules:
- always_use_package_imports
+1 -2
View File
@@ -4,5 +4,4 @@ targets:
json_serializable:
options:
explicit_to_json: true
field_rename: snake
any_map: true
field_rename: snake
@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
location = "self:">
</FileRef>
</Workspace>
+35 -33
View File
@@ -2,12 +2,9 @@ import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
Future<void> main() async {
/// Create a new instance of [StreamChatClient] passing the apikey obtained from your
/// project dashboard.
final client = StreamChatClient(
'b67pax5b2wdq',
logLevel: Level.INFO,
);
/// Create a new instance of [StreamChatClient]
/// by passing the apikey obtained from your project dashboard.
final client = StreamChatClient('b67pax5b2wdq', logLevel: Level.INFO);
/// Set the current user. In a production scenario, this should be done using
/// a backend to generate a user token using our server SDK.
@@ -21,7 +18,7 @@ Future<void> main() async {
'https://getstream.io/random_png/?id=cool-shadow-7&amp;name=Cool+shadow',
},
),
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo',
'''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo''',
);
/// Creates a channel using the type `messaging` and `godevs`.
@@ -44,15 +41,16 @@ Future<void> main() async {
/// Example using Stream's Low Level Dart client.
class StreamExample extends StatelessWidget {
/// To initialize this example, an instance of [client] and [channel] is required.
/// To initialize this example, an instance of
/// [client] and [channel] is required.
const StreamExample({
Key key,
@required this.client,
@required this.channel,
Key? key,
required this.client,
required this.channel,
}) : super(key: key);
/// Instance of [StreamChatClient] we created earlier. This contains information about
/// our application and connection state.
/// Instance of [StreamChatClient] we created earlier.
/// This contains information about our application and connection state.
final StreamChatClient client;
/// The channel we'd like to observe and participate.
@@ -71,28 +69,31 @@ class StreamExample extends StatelessWidget {
/// containing the channel name and a [MessageView] displaying recent messages.
class HomeScreen extends StatelessWidget {
/// [HomeScreen] is constructed using the [Channel] we defined earlier.
const HomeScreen({Key key, @required this.channel}) : super(key: key);
const HomeScreen({
Key? key,
required this.channel,
}) : super(key: key);
/// Channel object containing the [Channel.id] we'd like to observe.
final Channel channel;
@override
Widget build(BuildContext context) {
final messages = channel.state.channelStateStream;
final messages = channel.state!.channelStateStream;
return Scaffold(
appBar: AppBar(
title: Text('Channel: ${channel.id}'),
),
body: SafeArea(
child: StreamBuilder<ChannelState>(
child: StreamBuilder<ChannelState?>(
stream: messages,
builder: (
BuildContext context,
AsyncSnapshot<ChannelState> snapshot,
AsyncSnapshot<ChannelState?> snapshot,
) {
if (snapshot.hasData && snapshot.data != null) {
return MessageView(
messages: snapshot.data.messages.reversed.toList(),
messages: snapshot.data!.messages.reversed.toList(),
channel: channel,
);
} else if (snapshot.hasError) {
@@ -104,8 +105,8 @@ class HomeScreen extends StatelessWidget {
}
return const Center(
child: SizedBox(
width: 100.0,
height: 100.0,
width: 100,
height: 100,
child: CircularProgressIndicator(),
),
);
@@ -121,9 +122,9 @@ class HomeScreen extends StatelessWidget {
class MessageView extends StatefulWidget {
/// Message takes the latest list of messages and the current channel.
const MessageView({
Key key,
@required this.messages,
@required this.channel,
Key? key,
required this.messages,
required this.channel,
}) : super(key: key);
/// List of messages sent in the given channel.
@@ -137,8 +138,8 @@ class MessageView extends StatefulWidget {
}
class _MessageViewState extends State<MessageView> {
TextEditingController _controller;
ScrollController _scrollController;
late final TextEditingController _controller;
late final ScrollController _scrollController;
List<Message> get _messages => widget.messages;
@@ -176,20 +177,20 @@ class _MessageViewState extends State<MessageView> {
reverse: true,
itemBuilder: (BuildContext context, int index) {
final item = _messages[index];
if (item.user.id == widget.channel.client.uid) {
if (item.user?.id == widget.channel.client.uid) {
return Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(item.text),
padding: const EdgeInsets.all(8),
child: Text(item.text ?? ''),
),
);
} else {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(item.text),
padding: const EdgeInsets.all(8),
child: Text(item.text ?? ''),
),
);
}
@@ -197,7 +198,7 @@ class _MessageViewState extends State<MessageView> {
),
),
Padding(
padding: const EdgeInsets.all(8.0),
padding: const EdgeInsets.all(8),
child: Row(
children: [
Expanded(
@@ -245,7 +246,8 @@ class _MessageViewState extends State<MessageView> {
}
}
/// Helper extension for quickly retrieving the current user id from a [StreamChatClient].
/// Helper extension for quickly retrieving
/// the current user id from a [StreamChatClient].
extension on StreamChatClient {
String get uid => state.user.id;
String get uid => state.user!.id;
}
+6 -5
View File
@@ -1,21 +1,22 @@
name: example
description: A new Flutter project.
publish_to: 'none'
publish_to: "none"
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'
dependencies:
cupertino_icons: ^1.0.0
flutter:
sdk: flutter
cupertino_icons: ^1.0.0
stream_chat:
stream_chat:
path: ../
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
uses-material-design: true
File diff suppressed because it is too large Load Diff
+12 -11
View File
@@ -34,7 +34,7 @@ class SortOption<T> {
/// Sorting field Comparator required for offline sorting
@JsonKey(ignore: true)
final Comparator<T> comparator;
final Comparator<T>? comparator;
/// Serialize model to json
Map<String, dynamic> toJson() => _$SortOptionToJson(this);
@@ -70,31 +70,31 @@ class PaginationParams {
/// Filter on ids greater than the given value.
@JsonKey(name: 'id_gt')
final String greaterThan;
final String? greaterThan;
/// Filter on ids greater than or equal to the given value.
@JsonKey(name: 'id_gte')
final String greaterThanOrEqual;
final String? greaterThanOrEqual;
/// Filter on ids smaller than the given value.
@JsonKey(name: 'id_lt')
final String lessThan;
final String? lessThan;
/// Filter on ids smaller than or equal to the given value.
@JsonKey(name: 'id_lte')
final String lessThanOrEqual;
final String? lessThanOrEqual;
/// Serialize model to json
Map<String, dynamic> toJson() => _$PaginationParamsToJson(this);
/// Creates a copy of [PaginationParams] with specified attributes overridden.
PaginationParams copyWith({
int limit,
int offset,
String greaterThan,
String greaterThanOrEqual,
String lessThan,
String lessThanOrEqual,
int? limit,
int? offset,
String? greaterThan,
String? greaterThanOrEqual,
String? lessThan,
String? lessThanOrEqual,
}) =>
PaginationParams(
limit: limit ?? this.limit,
@@ -106,6 +106,7 @@ class PaginationParams {
);
@override
@JsonKey(ignore: true)
int get hashCode =>
runtimeType.hashCode ^
limit.hashCode ^
@@ -13,7 +13,10 @@ Map<String, dynamic> _$SortOptionToJson<T>(SortOption<T> instance) =>
};
Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) {
final val = <String, dynamic>{};
final val = <String, dynamic>{
'limit': instance.limit,
'offset': instance.offset,
};
void writeNotNull(String key, dynamic value) {
if (value != null) {
@@ -21,8 +24,6 @@ Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) {
}
}
writeNotNull('limit', instance.limit);
writeNotNull('offset', instance.offset);
writeNotNull('id_gt', instance.greaterThan);
writeNotNull('id_gte', instance.greaterThanOrEqual);
writeNotNull('id_lt', instance.lessThan);
+70 -51
View File
@@ -13,14 +13,15 @@ import 'package:stream_chat/src/models/user.dart';
part 'responses.g.dart';
class _BaseResponse {
String duration;
String? duration;
}
/// Model response for [StreamChatClient.resync] api call
@JsonSerializable(createToJson: false)
class SyncResponse extends _BaseResponse {
/// The list of events
List<Event> events;
@JsonKey(defaultValue: [])
late List<Event> events;
/// Create a new instance from a json
static SyncResponse fromJson(Map<String, dynamic> json) =>
@@ -31,7 +32,8 @@ class SyncResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class QueryChannelsResponse extends _BaseResponse {
/// List of channels state returned by the query
List<ChannelState> channels;
@JsonKey(defaultValue: [])
late List<ChannelState> channels;
/// Create a new instance from a json
static QueryChannelsResponse fromJson(Map<String, dynamic> json) =>
@@ -41,8 +43,8 @@ class QueryChannelsResponse extends _BaseResponse {
/// Model response for [StreamChatClient.queryChannels] api call
@JsonSerializable(createToJson: false)
class TranslateMessageResponse extends _BaseResponse {
/// List of channels state returned by the query
TranslatedMessage message;
/// Translated message
late TranslatedMessage message;
/// Create a new instance from a json
static TranslateMessageResponse fromJson(Map<String, dynamic> json) =>
@@ -53,7 +55,8 @@ class TranslateMessageResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class QueryMembersResponse extends _BaseResponse {
/// List of channels state returned by the query
List<Member> members;
@JsonKey(defaultValue: [])
late List<Member> members;
/// Create a new instance from a json
static QueryMembersResponse fromJson(Map<String, dynamic> json) =>
@@ -64,7 +67,8 @@ class QueryMembersResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class QueryUsersResponse extends _BaseResponse {
/// List of users returned by the query
List<User> users;
@JsonKey(defaultValue: [])
late List<User> users;
/// Create a new instance from a json
static QueryUsersResponse fromJson(Map<String, dynamic> json) =>
@@ -75,7 +79,8 @@ class QueryUsersResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class QueryReactionsResponse extends _BaseResponse {
/// List of reactions returned by the query
List<Reaction> reactions;
@JsonKey(defaultValue: [])
late List<Reaction> reactions;
/// Create a new instance from a json
static QueryReactionsResponse fromJson(Map<String, dynamic> json) =>
@@ -86,7 +91,8 @@ class QueryReactionsResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class QueryRepliesResponse extends _BaseResponse {
/// List of messages returned by the api call
List<Message> messages;
@JsonKey(defaultValue: [])
late List<Message> messages;
/// Create a new instance from a json
static QueryRepliesResponse fromJson(Map<String, dynamic> json) =>
@@ -97,7 +103,8 @@ class QueryRepliesResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class ListDevicesResponse extends _BaseResponse {
/// List of user devices
List<Device> devices;
@JsonKey(defaultValue: [])
late List<Device> devices;
/// Create a new instance from a json
static ListDevicesResponse fromJson(Map<String, dynamic> json) =>
@@ -108,7 +115,7 @@ class ListDevicesResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class SendFileResponse extends _BaseResponse {
/// The url of the uploaded file
String file;
late String file;
/// Create a new instance from a json
static SendFileResponse fromJson(Map<String, dynamic> json) =>
@@ -119,7 +126,7 @@ class SendFileResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class SendImageResponse extends _BaseResponse {
/// The url of the uploaded file
String file;
late String file;
/// Create a new instance from a json
static SendImageResponse fromJson(Map<String, dynamic> json) =>
@@ -130,10 +137,10 @@ class SendImageResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class SendReactionResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
late Message message;
/// The reaction created by the api call
Reaction reaction;
late Reaction reaction;
/// Create a new instance from a json
static SendReactionResponse fromJson(Map<String, dynamic> json) =>
@@ -144,10 +151,10 @@ class SendReactionResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class ConnectGuestUserResponse extends _BaseResponse {
/// Guest user access token
String accessToken;
late String accessToken;
/// Guest user
User user;
late User user;
/// Create a new instance from a json
static ConnectGuestUserResponse fromJson(Map<String, dynamic> json) =>
@@ -158,7 +165,8 @@ class ConnectGuestUserResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class UpdateUsersResponse extends _BaseResponse {
/// Updated users
Map<String, User> users;
@JsonKey(defaultValue: {})
late Map<String, User> users;
/// Create a new instance from a json
static UpdateUsersResponse fromJson(Map<String, dynamic> json) =>
@@ -169,7 +177,7 @@ class UpdateUsersResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class UpdateMessageResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
late Message message;
/// Create a new instance from a json
static UpdateMessageResponse fromJson(Map<String, dynamic> json) =>
@@ -180,7 +188,7 @@ class UpdateMessageResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class SendMessageResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
late Message message;
/// Create a new instance from a json
static SendMessageResponse fromJson(Map<String, dynamic> json) =>
@@ -191,17 +199,17 @@ class SendMessageResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class GetMessageResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
late Message message;
/// Channel of the message
ChannelModel channel;
ChannelModel? channel;
/// Create a new instance from a json
static GetMessageResponse fromJson(Map<String, dynamic> json) {
final res = _$GetMessageResponseFromJson(json);
final jsonChannel = res.message?.extraData?.remove('channel');
final jsonChannel = res.message.extraData.remove('channel');
if (jsonChannel != null) {
res.channel = ChannelModel.fromJson(jsonChannel);
res.channel = ChannelModel.fromJson(jsonChannel as Map<String, dynamic>);
}
return res;
}
@@ -211,7 +219,8 @@ class GetMessageResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class SearchMessagesResponse extends _BaseResponse {
/// List of messages returned by the api call
List<GetMessageResponse> results;
@JsonKey(defaultValue: [])
late List<GetMessageResponse> results;
/// Create a new instance from a json
static SearchMessagesResponse fromJson(Map<String, dynamic> json) =>
@@ -222,7 +231,8 @@ class SearchMessagesResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class GetMessagesByIdResponse extends _BaseResponse {
/// Message returned by the api call
List<Message> messages;
@JsonKey(defaultValue: [])
late List<Message> messages;
/// Create a new instance from a json
static GetMessagesByIdResponse fromJson(Map<String, dynamic> json) =>
@@ -233,13 +243,13 @@ class GetMessagesByIdResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class UpdateChannelResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// Channel members
List<Member> members;
List<Member>? members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static UpdateChannelResponse fromJson(Map<String, dynamic> json) =>
@@ -250,10 +260,10 @@ class UpdateChannelResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class PartialUpdateChannelResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// Channel members
List<Member> members;
List<Member>? members;
/// Create a new instance from a json
static PartialUpdateChannelResponse fromJson(Map<String, dynamic> json) =>
@@ -264,13 +274,14 @@ class PartialUpdateChannelResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class InviteMembersResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// Channel members
List<Member> members;
@JsonKey(defaultValue: [])
late List<Member> members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static InviteMembersResponse fromJson(Map<String, dynamic> json) =>
@@ -281,13 +292,14 @@ class InviteMembersResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class RemoveMembersResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// Channel members
List<Member> members;
@JsonKey(defaultValue: [])
late List<Member> members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static RemoveMembersResponse fromJson(Map<String, dynamic> json) =>
@@ -298,7 +310,7 @@ class RemoveMembersResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class SendActionResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static SendActionResponse fromJson(Map<String, dynamic> json) =>
@@ -309,13 +321,14 @@ class SendActionResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class AddMembersResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// Channel members
List<Member> members;
@JsonKey(defaultValue: [])
late List<Member> members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static AddMembersResponse fromJson(Map<String, dynamic> json) =>
@@ -326,13 +339,14 @@ class AddMembersResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class AcceptInviteResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// Channel members
List<Member> members;
@JsonKey(defaultValue: [])
late List<Member> members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static AcceptInviteResponse fromJson(Map<String, dynamic> json) =>
@@ -343,13 +357,14 @@ class AcceptInviteResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class RejectInviteResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// Channel members
List<Member> members;
@JsonKey(defaultValue: [])
late List<Member> members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static RejectInviteResponse fromJson(Map<String, dynamic> json) =>
@@ -368,19 +383,23 @@ class EmptyResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class ChannelStateResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// List of messages returned by the api call
List<Message> messages;
@JsonKey(defaultValue: [])
late List<Message> messages;
/// Channel members
List<Member> members;
@JsonKey(defaultValue: [])
late List<Member> members;
/// Number of users watching the channel
int watcherCount;
@JsonKey(defaultValue: 0)
late int watcherCount;
/// List of read states
List<Read> read;
@JsonKey(defaultValue: [])
late List<Read> read;
/// Create a new instance from a json
static ChannelStateResponse fromJson(Map<String, dynamic> json) =>
+170 -290
View File
@@ -6,394 +6,274 @@ part of 'responses.dart';
// JsonSerializableGenerator
// **************************************************************************
SyncResponse _$SyncResponseFromJson(Map json) {
SyncResponse _$SyncResponseFromJson(Map<String, dynamic> json) {
return SyncResponse()
..duration = json['duration'] as String
..events = (json['events'] as List)
?.map((e) => e == null
? null
: Event.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..events = (json['events'] as List<dynamic>?)
?.map((e) => Event.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
QueryChannelsResponse _$QueryChannelsResponseFromJson(Map json) {
QueryChannelsResponse _$QueryChannelsResponseFromJson(
Map<String, dynamic> json) {
return QueryChannelsResponse()
..duration = json['duration'] as String
..channels = (json['channels'] as List)
?.map((e) => e == null ? null : ChannelState.fromJson(e as Map))
?.toList();
..duration = json['duration'] as String?
..channels = (json['channels'] as List<dynamic>?)
?.map((e) => ChannelState.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
TranslateMessageResponse _$TranslateMessageResponseFromJson(Map json) {
TranslateMessageResponse _$TranslateMessageResponseFromJson(
Map<String, dynamic> json) {
return TranslateMessageResponse()
..duration = json['duration'] as String
..message = json['message'] == null
? null
: TranslatedMessage.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
..duration = json['duration'] as String?
..message =
TranslatedMessage.fromJson(json['message'] as Map<String, dynamic>);
}
QueryMembersResponse _$QueryMembersResponseFromJson(Map json) {
QueryMembersResponse _$QueryMembersResponseFromJson(Map<String, dynamic> json) {
return QueryMembersResponse()
..duration = json['duration'] as String
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
QueryUsersResponse _$QueryUsersResponseFromJson(Map json) {
QueryUsersResponse _$QueryUsersResponseFromJson(Map<String, dynamic> json) {
return QueryUsersResponse()
..duration = json['duration'] as String
..users = (json['users'] as List)
?.map((e) => e == null
? null
: User.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..users = (json['users'] as List<dynamic>?)
?.map((e) => User.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
QueryReactionsResponse _$QueryReactionsResponseFromJson(Map json) {
QueryReactionsResponse _$QueryReactionsResponseFromJson(
Map<String, dynamic> json) {
return QueryReactionsResponse()
..duration = json['duration'] as String
..reactions = (json['reactions'] as List)
?.map((e) => e == null
? null
: Reaction.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..reactions = (json['reactions'] as List<dynamic>?)
?.map((e) => Reaction.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
QueryRepliesResponse _$QueryRepliesResponseFromJson(Map json) {
QueryRepliesResponse _$QueryRepliesResponseFromJson(Map<String, dynamic> json) {
return QueryRepliesResponse()
..duration = json['duration'] as String
..messages = (json['messages'] as List)
?.map((e) => e == null
? null
: Message.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..messages = (json['messages'] as List<dynamic>?)
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
ListDevicesResponse _$ListDevicesResponseFromJson(Map json) {
ListDevicesResponse _$ListDevicesResponseFromJson(Map<String, dynamic> json) {
return ListDevicesResponse()
..duration = json['duration'] as String
..devices = (json['devices'] as List)
?.map((e) => e == null
? null
: Device.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..devices = (json['devices'] as List<dynamic>?)
?.map((e) => Device.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
SendFileResponse _$SendFileResponseFromJson(Map json) {
SendFileResponse _$SendFileResponseFromJson(Map<String, dynamic> json) {
return SendFileResponse()
..duration = json['duration'] as String
..duration = json['duration'] as String?
..file = json['file'] as String;
}
SendImageResponse _$SendImageResponseFromJson(Map json) {
SendImageResponse _$SendImageResponseFromJson(Map<String, dynamic> json) {
return SendImageResponse()
..duration = json['duration'] as String
..duration = json['duration'] as String?
..file = json['file'] as String;
}
SendReactionResponse _$SendReactionResponseFromJson(Map json) {
SendReactionResponse _$SendReactionResponseFromJson(Map<String, dynamic> json) {
return SendReactionResponse()
..duration = json['duration'] as String
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..reaction = json['reaction'] == null
? null
: Reaction.fromJson((json['reaction'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
..duration = json['duration'] as String?
..message = Message.fromJson(json['message'] as Map<String, dynamic>)
..reaction = Reaction.fromJson(json['reaction'] as Map<String, dynamic>);
}
ConnectGuestUserResponse _$ConnectGuestUserResponseFromJson(Map json) {
ConnectGuestUserResponse _$ConnectGuestUserResponseFromJson(
Map<String, dynamic> json) {
return ConnectGuestUserResponse()
..duration = json['duration'] as String
..duration = json['duration'] as String?
..accessToken = json['access_token'] as String
..user = json['user'] == null
? null
: User.fromJson((json['user'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
..user = User.fromJson(json['user'] as Map<String, dynamic>);
}
UpdateUsersResponse _$UpdateUsersResponseFromJson(Map json) {
UpdateUsersResponse _$UpdateUsersResponseFromJson(Map<String, dynamic> json) {
return UpdateUsersResponse()
..duration = json['duration'] as String
..users = (json['users'] as Map)?.map(
(k, e) => MapEntry(
k as String,
e == null
? null
: User.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
))),
);
..duration = json['duration'] as String?
..users = (json['users'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, User.fromJson(e as Map<String, dynamic>)),
) ??
{};
}
UpdateMessageResponse _$UpdateMessageResponseFromJson(Map json) {
UpdateMessageResponse _$UpdateMessageResponseFromJson(
Map<String, dynamic> json) {
return UpdateMessageResponse()
..duration = json['duration'] as String
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
..duration = json['duration'] as String?
..message = Message.fromJson(json['message'] as Map<String, dynamic>);
}
SendMessageResponse _$SendMessageResponseFromJson(Map json) {
SendMessageResponse _$SendMessageResponseFromJson(Map<String, dynamic> json) {
return SendMessageResponse()
..duration = json['duration'] as String
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
..duration = json['duration'] as String?
..message = Message.fromJson(json['message'] as Map<String, dynamic>);
}
GetMessageResponse _$GetMessageResponseFromJson(Map json) {
GetMessageResponse _$GetMessageResponseFromJson(Map<String, dynamic> json) {
return GetMessageResponse()
..duration = json['duration'] as String
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..duration = json['duration'] as String?
..message = Message.fromJson(json['message'] as Map<String, dynamic>)
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: ChannelModel.fromJson(json['channel'] as Map<String, dynamic>);
}
SearchMessagesResponse _$SearchMessagesResponseFromJson(Map json) {
SearchMessagesResponse _$SearchMessagesResponseFromJson(
Map<String, dynamic> json) {
return SearchMessagesResponse()
..duration = json['duration'] as String
..results = (json['results'] as List)
?.map((e) => e == null ? null : GetMessageResponse.fromJson(e as Map))
?.toList();
..duration = json['duration'] as String?
..results = (json['results'] as List<dynamic>?)
?.map((e) => GetMessageResponse.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
GetMessagesByIdResponse _$GetMessagesByIdResponseFromJson(Map json) {
GetMessagesByIdResponse _$GetMessagesByIdResponseFromJson(
Map<String, dynamic> json) {
return GetMessagesByIdResponse()
..duration = json['duration'] as String
..messages = (json['messages'] as List)
?.map((e) => e == null
? null
: Message.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..messages = (json['messages'] as List<dynamic>?)
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
UpdateChannelResponse _$UpdateChannelResponseFromJson(Map json) {
UpdateChannelResponse _$UpdateChannelResponseFromJson(
Map<String, dynamic> json) {
return UpdateChannelResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList()
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: Message.fromJson(json['message'] as Map<String, dynamic>);
}
PartialUpdateChannelResponse _$PartialUpdateChannelResponseFromJson(Map json) {
PartialUpdateChannelResponse _$PartialUpdateChannelResponseFromJson(
Map<String, dynamic> json) {
return PartialUpdateChannelResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList();
}
InviteMembersResponse _$InviteMembersResponseFromJson(Map json) {
InviteMembersResponse _$InviteMembersResponseFromJson(
Map<String, dynamic> json) {
return InviteMembersResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[]
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: Message.fromJson(json['message'] as Map<String, dynamic>);
}
RemoveMembersResponse _$RemoveMembersResponseFromJson(Map json) {
RemoveMembersResponse _$RemoveMembersResponseFromJson(
Map<String, dynamic> json) {
return RemoveMembersResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[]
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: Message.fromJson(json['message'] as Map<String, dynamic>);
}
SendActionResponse _$SendActionResponseFromJson(Map json) {
SendActionResponse _$SendActionResponseFromJson(Map<String, dynamic> json) {
return SendActionResponse()
..duration = json['duration'] as String
..duration = json['duration'] as String?
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: Message.fromJson(json['message'] as Map<String, dynamic>);
}
AddMembersResponse _$AddMembersResponseFromJson(Map json) {
AddMembersResponse _$AddMembersResponseFromJson(Map<String, dynamic> json) {
return AddMembersResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[]
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: Message.fromJson(json['message'] as Map<String, dynamic>);
}
AcceptInviteResponse _$AcceptInviteResponseFromJson(Map json) {
AcceptInviteResponse _$AcceptInviteResponseFromJson(Map<String, dynamic> json) {
return AcceptInviteResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[]
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: Message.fromJson(json['message'] as Map<String, dynamic>);
}
RejectInviteResponse _$RejectInviteResponseFromJson(Map json) {
RejectInviteResponse _$RejectInviteResponseFromJson(Map<String, dynamic> json) {
return RejectInviteResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[]
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: Message.fromJson(json['message'] as Map<String, dynamic>);
}
EmptyResponse _$EmptyResponseFromJson(Map json) {
return EmptyResponse()..duration = json['duration'] as String;
EmptyResponse _$EmptyResponseFromJson(Map<String, dynamic> json) {
return EmptyResponse()..duration = json['duration'] as String?;
}
ChannelStateResponse _$ChannelStateResponseFromJson(Map json) {
ChannelStateResponse _$ChannelStateResponseFromJson(Map<String, dynamic> json) {
return ChannelStateResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..messages = (json['messages'] as List)
?.map((e) => e == null
? null
: Message.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..watcherCount = json['watcher_count'] as int
..read = (json['read'] as List)
?.map((e) => e == null
? null
: Read.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..messages = (json['messages'] as List<dynamic>?)
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
.toList() ??
[]
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[]
..watcherCount = json['watcher_count'] as int? ?? 0
..read = (json['read'] as List<dynamic>?)
?.map((e) => Read.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
@@ -1,4 +1,3 @@
import 'package:meta/meta.dart';
import 'package:stream_chat/src/client.dart';
import 'package:stream_chat/src/exceptions.dart';
@@ -6,30 +5,30 @@ import 'package:stream_chat/src/exceptions.dart';
class RetryPolicy {
/// Instantiate a new RetryPolicy
RetryPolicy({
@required this.shouldRetry,
@required this.retryTimeout,
this.attempt,
required this.shouldRetry,
required this.retryTimeout,
this.attempt = 0,
});
/// The number of attempts tried so far
int attempt = 0;
/// This function evaluates if we should retry the failure
final bool Function(StreamChatClient client, int attempt, ApiError apiError)
final bool Function(StreamChatClient client, int attempt, ApiError? apiError)
shouldRetry;
/// In the case that we want to retry a failed request the retryTimeout
/// method is called to determine the timeout
final Duration Function(
StreamChatClient client, int attempt, ApiError apiError) retryTimeout;
StreamChatClient client, int attempt, ApiError? apiError) retryTimeout;
/// Creates a copy of [RetryPolicy] with specified attributes overridden.
RetryPolicy copyWith({
bool Function(StreamChatClient client, int attempt, ApiError apiError)
bool Function(StreamChatClient client, int attempt, ApiError? apiError)?
shouldRetry,
Duration Function(StreamChatClient client, int attempt, ApiError apiError)
Duration Function(StreamChatClient client, int attempt, ApiError? apiError)?
retryTimeout,
int attempt,
int? attempt,
}) =>
RetryPolicy(
retryTimeout: retryTimeout ?? this.retryTimeout,
@@ -2,7 +2,6 @@ import 'dart:async';
import 'package:collection/collection.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:stream_chat/src/api/channel.dart';
import 'package:stream_chat/src/api/retry_policy.dart';
import 'package:stream_chat/src/event_type.dart';
@@ -14,7 +13,7 @@ import 'package:stream_chat/stream_chat.dart';
class RetryQueue {
/// Instantiate a new RetryQueue object
RetryQueue({
@required this.channel,
required this.channel,
this.logger,
}) {
_retryPolicy = channel.client.retryPolicy;
@@ -28,14 +27,14 @@ class RetryQueue {
final Channel channel;
/// The logger associated to this queue
final Logger logger;
final Logger? logger;
final _subscriptions = <StreamSubscription>[];
void _listenConnectionRecovered() {
_subscriptions
.add(channel.client.on(EventType.connectionRecovered).listen((event) {
if (!_isRetrying && event.online) {
if (!_isRetrying && event.online!) {
_startRetrying();
}
}));
@@ -43,12 +42,13 @@ class RetryQueue {
final HeapPriorityQueue<Message> _messageQueue = HeapPriorityQueue(_byDate);
bool _isRetrying = false;
RetryPolicy _retryPolicy;
RetryPolicy? _retryPolicy;
/// Add a list of messages
void add(List<Message> messages) {
logger?.info('added ${messages.length} messages');
final messageList = _messageQueue.toList();
_messageQueue.addAll(messages
.where((element) => !messageList.any((m) => m.id == element.id)));
@@ -60,7 +60,7 @@ class RetryQueue {
Future<void> _startRetrying() async {
logger?.info('start retrying');
_isRetrying = true;
final retryPolicy = _retryPolicy.copyWith(attempt: 0);
final retryPolicy = _retryPolicy!.copyWith(attempt: 0);
while (_messageQueue.isNotEmpty) {
final message = _messageQueue.first;
@@ -72,9 +72,9 @@ class RetryQueue {
logger?.info('now ${_messageQueue.length} messages in the queue');
retryPolicy.attempt = 0;
} catch (error) {
ApiError apiError;
ApiError? apiError;
if (error is DioError) {
if (error.type == DioErrorType.RESPONSE) {
if (error.type == DioErrorType.response) {
_messageQueue.remove(message);
return;
}
@@ -84,7 +84,7 @@ class RetryQueue {
);
} else if (error is ApiError) {
apiError = error;
if (apiError.status?.toString()?.startsWith('4') == true) {
if (apiError.status?.toString().startsWith('4') == true) {
_messageQueue.remove(message);
return;
}
@@ -101,6 +101,7 @@ class RetryQueue {
}
retryPolicy.attempt++;
final timeout = retryPolicy.retryTimeout(
channel.client,
retryPolicy.attempt,
@@ -112,13 +113,13 @@ class RetryQueue {
_isRetrying = false;
}
void _sendFailedEvent(Message message) {
final newStatus = message.status == MessageSendingStatus.sending
void _sendFailedEvent(Message? message) {
final newStatus = message!.status == MessageSendingStatus.sending
? MessageSendingStatus.failed
: (message.status == MessageSendingStatus.updating
? MessageSendingStatus.failed_update
: MessageSendingStatus.failed_delete);
channel.state.addMessage(message.copyWith(
channel.state!.addMessage(message.copyWith(
status: newStatus,
));
}
@@ -141,20 +142,24 @@ class RetryQueue {
final messageList = _messageQueue.toList();
if (event.message != null) {
final messageIndex =
messageList.indexWhere((m) => m.id == event.message.id);
messageList.indexWhere((m) => m.id == event.message!.id);
if (messageIndex == -1 &&
[
MessageSendingStatus.failed_update,
MessageSendingStatus.failed,
MessageSendingStatus.failed_delete,
].contains(event.message.status)) {
].contains(event.message!.status)) {
logger?.info('add message from events');
add([event.message]);
final m = event.message;
if (m != null) {
add([m]);
}
} else if (messageIndex != -1 &&
[
MessageSendingStatus.sent,
null,
].contains(event.message.status)) {
].contains(event.message!.status)) {
_messageQueue.remove(messageList[messageIndex]);
}
}
@@ -171,10 +176,14 @@ class RetryQueue {
final date1 = _getMessageDate(m1);
final date2 = _getMessageDate(m2);
if (date1 == null || date2 == null) {
return 0;
}
return date1.compareTo(date2);
}
static DateTime _getMessageDate(Message m1) {
static DateTime? _getMessageDate(Message m1) {
switch (m1.status) {
case MessageSendingStatus.failed_delete:
case MessageSendingStatus.deleting:
@@ -3,5 +3,5 @@ import 'package:web_socket_channel/web_socket_channel.dart';
/// Html version of websocket implementation
/// Used in Flutter web version
WebSocketChannel connectWebSocket(String url, {Iterable<String> protocols}) =>
WebSocketChannel connectWebSocket(String url, {Iterable<String>? protocols}) =>
HtmlWebSocketChannel.connect(url, protocols: protocols);
@@ -3,5 +3,5 @@ import 'package:web_socket_channel/web_socket_channel.dart';
/// IO version of websocket implementation
/// Used in Flutter mobile version
WebSocketChannel connectWebSocket(String url, {Iterable<String> protocols}) =>
WebSocketChannel connectWebSocket(String url, {Iterable<String>? protocols}) =>
IOWebSocketChannel.connect(url, protocols: protocols);
@@ -3,7 +3,7 @@ import 'package:web_socket_channel/web_socket_channel.dart';
/// Stub version of websocket implementation
/// Used just for conditional library import
WebSocketChannel connectWebSocket(String url,
{Iterable<String> protocols,
Map<String, dynamic> headers,
Duration pingInterval}) =>
{Iterable<String>? protocols,
Map<String, dynamic>? headers,
Duration? pingInterval}) =>
throw UnimplementedError();
+36 -38
View File
@@ -16,8 +16,8 @@ typedef EventHandler = void Function(Event);
/// Typedef used for connecting to a websocket. Method returns a
/// [WebSocketChannel] and accepts a connection [url] and an optional
/// [Iterable] of `protocols`.
typedef ConnectWebSocket = WebSocketChannel Function(String url,
{Iterable<String> protocols});
typedef ConnectWebSocket = WebSocketChannel Function(String? url,
{Iterable<String>? protocols});
// TODO: parse error even
// TODO: if parsing an error into an event fails we should not hide the
@@ -27,11 +27,11 @@ class WebSocket {
/// Creates a new websocket
/// To connect the WS call [connect]
WebSocket({
@required this.baseUrl,
this.user,
this.connectParams,
this.connectPayload,
this.handler,
required this.baseUrl,
required this.user,
required this.handler,
this.connectParams = const {},
this.connectPayload = const {},
this.logger,
this.connectFunc,
this.reconnectionMonitorInterval = 1,
@@ -78,12 +78,12 @@ class WebSocket {
final EventHandler handler;
/// A WS specific logger instance
final Logger logger;
final Logger? logger;
/// Connection function
/// Used only for testing purpose
@visibleForTesting
final ConnectWebSocket connectFunc;
final ConnectWebSocket? connectFunc;
/// Interval of the reconnection monitor timer
/// This checks that it received a new event in the last
@@ -107,43 +107,43 @@ class WebSocket {
_connectionStatusController.add(status);
/// The current connection status value
ConnectionStatus get connectionStatus => _connectionStatusController.value;
ConnectionStatus? get connectionStatus => _connectionStatusController.value;
/// This notifies of connection status changes
Stream<ConnectionStatus> get connectionStatusStream =>
_connectionStatusController.stream;
String _path;
late String _path;
int _retryAttempt = 1;
WebSocketChannel _channel;
Timer _healthCheck, _reconnectionMonitor;
DateTime _lastEventAt;
late WebSocketChannel _channel;
Timer? _healthCheck, _reconnectionMonitor;
DateTime? _lastEventAt;
bool _manuallyDisconnected = false;
bool _connecting = false;
bool _reconnecting = false;
Event _decodeEvent(String source) => Event.fromJson(json.decode(source));
Completer<Event> _connectionCompleter = Completer<Event>();
Completer<Event?> _connectionCompleter = Completer<Event?>();
/// Connect the WS using the parameters passed in the constructor
Future<Event> connect() {
Future<Event?> connect() async {
_manuallyDisconnected = false;
if (_connecting) {
logger.severe('already connecting');
logger?.severe('already connecting');
return null;
}
_connecting = true;
_connectionStatus = ConnectionStatus.connecting;
logger.info('connecting to $_path');
logger?.info('connecting to $_path');
_channel =
connectFunc?.call(_path) ?? WebSocketChannel.connect(Uri.parse(_path));
_channel.stream.listen(
(data) {
(data) async {
final jsonData = json.decode(data);
if (jsonData['error'] != null) {
return _onConnectionError(jsonData['error']);
@@ -153,9 +153,7 @@ class WebSocket {
onError: (error, stacktrace) {
_onConnectionError(error, stacktrace);
},
onDone: () {
_onDone();
},
onDone: _onDone,
);
return _connectionCompleter.future;
}
@@ -166,7 +164,7 @@ class WebSocket {
return;
}
logger.info('connection closed | closeCode: ${_channel.closeCode} | '
logger?.info('connection closed | closeCode: ${_channel.closeCode} | '
'closedReason: ${_channel.closeReason}');
if (!_reconnecting) {
@@ -180,10 +178,10 @@ class WebSocket {
}
final event = _decodeEvent(data);
logger.info('received new event: $data');
logger?.info('received new event: $data');
if (_lastEventAt == null) {
logger.info('connection estabilished');
logger?.info('connection estabilished');
_connecting = false;
_reconnecting = false;
_lastEventAt = DateTime.now();
@@ -204,9 +202,9 @@ class WebSocket {
}
Future<void> _onConnectionError(error, [stacktrace]) async {
logger..severe('error connecting')..severe(error);
logger?..severe('error connecting')..severe(error);
if (stacktrace != null) {
logger.severe(stacktrace);
logger?.severe(stacktrace);
}
_connecting = false;
@@ -225,7 +223,7 @@ class WebSocket {
void _reconnectionTimer(_) {
final now = DateTime.now();
if (_lastEventAt != null &&
now.difference(_lastEventAt).inSeconds > reconnectionMonitorTimeout) {
now.difference(_lastEventAt!).inSeconds > reconnectionMonitorTimeout) {
_channel.sink.close();
}
}
@@ -244,18 +242,18 @@ class WebSocket {
return;
}
if (_connecting) {
logger.info('already connecting');
logger?.info('already connecting');
return;
}
logger.info('reconnecting..');
logger?.info('reconnecting..');
_cancelTimers();
try {
await connect();
} catch (e) {
logger.log(Level.SEVERE, e.toString());
logger?.log(Level.SEVERE, e.toString());
}
await Future.delayed(
Duration(seconds: min(_retryAttempt * 5, 25)),
@@ -267,7 +265,7 @@ class WebSocket {
}
Future<void> _reconnect() async {
logger.info('reconnect');
logger?.info('reconnect');
if (!_reconnecting) {
_reconnecting = true;
_connectionStatus = ConnectionStatus.connecting;
@@ -279,20 +277,20 @@ class WebSocket {
void _cancelTimers() {
_lastEventAt = null;
if (_healthCheck != null) {
_healthCheck.cancel();
_healthCheck!.cancel();
}
if (_reconnectionMonitor != null) {
_reconnectionMonitor.cancel();
_reconnectionMonitor!.cancel();
}
}
void _healthCheckTimer(_) {
logger.info('sending health.check');
logger?.info('sending health.check');
_channel.sink.add("{'type': 'health.check'}");
}
void _startHealthCheck() {
logger.info('start health check monitor');
logger?.info('start health check monitor');
_healthCheck = Timer.periodic(
Duration(seconds: healthCheckInterval),
@@ -311,13 +309,13 @@ class WebSocket {
if (_manuallyDisconnected) {
return;
}
logger.info('disconnecting');
logger?.info('disconnecting');
_connectionCompleter = Completer();
_cancelTimers();
_reconnecting = false;
_manuallyDisconnected = true;
_connectionStatus = ConnectionStatus.disconnected;
await _connectionStatusController.close();
return _channel.sink.close();
await _channel.sink.close();
}
}
@@ -1,8 +1,8 @@
import 'package:dio/dio.dart';
import 'package:stream_chat/src/api/responses.dart';
import 'package:stream_chat/src/client.dart';
import 'package:stream_chat/src/models/attachment_file.dart';
import 'package:stream_chat/src/extensions/string_extension.dart';
import 'package:stream_chat/src/models/attachment_file.dart';
/// Class responsible for uploading images and files to a given channel
abstract class AttachmentFileUploader {
@@ -15,8 +15,8 @@ abstract class AttachmentFileUploader {
AttachmentFile image,
String channelId,
String channelType, {
ProgressCallback onSendProgress,
CancelToken cancelToken,
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
});
/// Uploads a [file] to the given channel.
@@ -28,8 +28,8 @@ abstract class AttachmentFileUploader {
AttachmentFile file,
String channelId,
String channelType, {
ProgressCallback onSendProgress,
CancelToken cancelToken,
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
});
/// Deletes a image using its [url] from the given channel.
@@ -40,7 +40,7 @@ abstract class AttachmentFileUploader {
String url,
String channelId,
String channelType, {
CancelToken cancelToken,
CancelToken? cancelToken,
});
/// Deletes a file using its [url] from the given channel.
@@ -51,7 +51,7 @@ abstract class AttachmentFileUploader {
String url,
String channelId,
String channelType, {
CancelToken cancelToken,
CancelToken? cancelToken,
});
}
@@ -67,22 +67,22 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
AttachmentFile file,
String channelId,
String channelType, {
ProgressCallback onSendProgress,
CancelToken cancelToken,
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
}) async {
final filename = file.path?.split('/')?.last ?? file.name;
final mimeType = filename.mimeType;
final filename = file.path?.split('/').last ?? file.name;
final mimeType = filename?.mimeType;
MultipartFile multiPartFile;
MultipartFile? multiPartFile;
if (file.path != null) {
multiPartFile = await MultipartFile.fromFile(
file.path,
file.path!,
filename: filename,
contentType: mimeType,
);
} else if (file.bytes != null) {
multiPartFile = MultipartFile.fromBytes(
file.bytes,
file.bytes!,
filename: filename,
contentType: mimeType,
);
@@ -104,22 +104,22 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
AttachmentFile file,
String channelId,
String channelType, {
ProgressCallback onSendProgress,
CancelToken cancelToken,
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
}) async {
final filename = file.path?.split('/')?.last ?? file.name;
final mimeType = filename.mimeType;
final filename = file.path?.split('/').last ?? file.name;
final mimeType = filename?.mimeType;
MultipartFile multiPartFile;
MultipartFile? multiPartFile;
if (file.path != null) {
multiPartFile = await MultipartFile.fromFile(
file.path,
file.path!,
filename: filename,
contentType: mimeType,
);
} else if (file.bytes != null) {
multiPartFile = MultipartFile.fromBytes(
file.bytes,
file.bytes!,
filename: filename,
contentType: mimeType,
);
@@ -141,7 +141,7 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
String url,
String channelId,
String channelType, {
CancelToken cancelToken,
CancelToken? cancelToken,
}) async {
final response = await _client.delete(
'/channels/$channelType/$channelId/image',
@@ -156,7 +156,7 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
String url,
String channelId,
String channelType, {
CancelToken cancelToken,
CancelToken? cancelToken,
}) async {
final response = await _client.delete(
'/channels/$channelType/$channelId/file',
File diff suppressed because it is too large Load Diff
@@ -2,11 +2,13 @@ import 'package:stream_chat/src/api/requests.dart';
import 'package:stream_chat/src/models/channel_model.dart';
import 'package:stream_chat/src/models/channel_state.dart';
import 'package:stream_chat/src/models/event.dart';
import 'package:stream_chat/src/models/filter.dart';
import 'package:stream_chat/src/models/member.dart';
import 'package:stream_chat/src/models/message.dart';
import 'package:stream_chat/src/models/reaction.dart';
import 'package:stream_chat/src/models/read.dart';
import 'package:stream_chat/src/models/user.dart';
import 'package:stream_chat/src/extensions/iterable_extension.dart';
/// A simple client used for persisting chat data locally.
abstract class ChatPersistenceClient {
@@ -20,14 +22,14 @@ abstract class ChatPersistenceClient {
/// Get stored replies by messageId
Future<List<Message>> getReplies(
String parentId, {
PaginationParams options,
PaginationParams? options,
});
/// Get stored connection event
Future<Event> getConnectionInfo();
Future<Event?> getConnectionInfo();
/// Get stored lastSyncAt
Future<DateTime> getLastSyncAt();
Future<DateTime?> getLastSyncAt();
/// Update stored connection event
Future<void> updateConnectionInfo(Event event);
@@ -39,7 +41,7 @@ abstract class ChatPersistenceClient {
Future<List<String>> getChannelCids();
/// Get stored [ChannelModel]s by providing channel [cid]
Future<ChannelModel> getChannelByCid(String cid);
Future<ChannelModel?> getChannelByCid(String cid);
/// Get stored channel [Member]s by providing channel [cid]
Future<List<Member>> getMembersByCid(String cid);
@@ -53,20 +55,20 @@ abstract class ChatPersistenceClient {
/// for filtering out messages
Future<List<Message>> getMessagesByCid(
String cid, {
PaginationParams messagePagination,
PaginationParams? messagePagination,
});
/// Get stored pinned [Message]s by providing channel [cid]
Future<List<Message>> getPinnedMessagesByCid(
String cid, {
PaginationParams messagePagination,
PaginationParams? messagePagination,
});
/// Get [ChannelState] data by providing channel [cid]
Future<ChannelState> getChannelStateByCid(
String cid, {
PaginationParams messagePagination,
PaginationParams pinnedMessagePagination,
PaginationParams? messagePagination,
PaginationParams? pinnedMessagePagination,
}) async {
final data = await Future.wait([
getMembersByCid(cid),
@@ -76,11 +78,11 @@ abstract class ChatPersistenceClient {
getPinnedMessagesByCid(cid, messagePagination: pinnedMessagePagination),
]);
return ChannelState(
members: data[0],
read: data[1],
channel: data[2],
messages: data[3],
pinnedMessages: data[4],
members: data[0] as List<Member>,
read: data[1] as List<Read>,
channel: data[2] as ChannelModel?,
messages: data[3] as List<Message>,
pinnedMessages: data[4] as List<Message>,
);
}
@@ -89,9 +91,9 @@ abstract class ChatPersistenceClient {
/// Optionally, pass [filter], [sort], [paginationParams]
/// for filtering out states.
Future<List<ChannelState>> getChannelStates({
Map<String, dynamic> filter,
List<SortOption<ChannelModel>> sort = const [],
PaginationParams paginationParams,
Filter? filter,
List<SortOption<ChannelModel>>? sort,
PaginationParams? paginationParams,
});
/// Update list of channel queries.
@@ -99,7 +101,7 @@ abstract class ChatPersistenceClient {
/// If [clearQueryCache] is true before the insert
/// the list of matching rows will be deleted
Future<void> updateChannelQueries(
Map<String, dynamic> filter,
Filter? filter,
List<String> cids, {
bool clearQueryCache = false,
});
@@ -180,8 +182,11 @@ abstract class ChatPersistenceClient {
.map((m) => m.id)
.toList(growable: false));
final cleanedChannelStates =
channelStates.where((it) => it.channel != null);
final deleteMembers = deleteMembersByCids(
channelStates.map((it) => it.channel.cid).toList(growable: false),
cleanedChannelStates.map((it) => it.channel!.cid).toList(growable: false),
);
await Future.wait([
@@ -189,58 +194,57 @@ abstract class ChatPersistenceClient {
deleteMembers,
]);
final channels =
channelStates.map((it) => it.channel).where((it) => it != null);
final channels = cleanedChannelStates.map((it) => it.channel).withNullifyer;
final reactions = channelStates
final reactions = cleanedChannelStates
.expand((it) => it.messages)
.expand((it) => [
if (it.ownReactions != null)
...it.ownReactions.where((r) => r.userId != null),
...it.ownReactions!.where((r) => r.userId != null),
if (it.latestReactions != null)
...it.latestReactions.where((r) => r.userId != null)
...it.latestReactions!.where((r) => r.userId != null),
])
.where((it) => it != null);
.withNullifyer;
final users = channelStates
final users = cleanedChannelStates
.map((cs) => [
cs.channel?.createdBy,
...cs.messages
?.map((m) => [
.map((m) => [
m.user,
if (m.latestReactions != null)
...m.latestReactions.map((r) => r.user),
...m.latestReactions!.map((r) => r.user),
if (m.ownReactions != null)
...m.ownReactions.map((r) => r.user),
...m.ownReactions!.map((r) => r.user),
])
?.expand((v) => v),
if (cs.read != null) ...cs.read.map((r) => r.user),
if (cs.members != null) ...cs.members.map((m) => m.user),
.expand((v) => v),
...cs.read.map((r) => r.user),
...cs.members.map((m) => m.user),
])
.expand((it) => it)
.where((it) => it != null);
.withNullifyer;
final updateMessagesFuture = channelStates.map((it) {
final cid = it.channel.cid;
final messages = it.messages.where((it) => it != null);
final updateMessagesFuture = cleanedChannelStates.map((it) {
final cid = it.channel!.cid;
final messages = it.messages;
return updateMessages(cid, messages.toList(growable: false));
}).toList(growable: false);
final updatePinnedMessagesFuture = channelStates.map((it) {
final cid = it.channel.cid;
final messages = it.pinnedMessages.where((it) => it != null);
final updatePinnedMessagesFuture = cleanedChannelStates.map((it) {
final cid = it.channel!.cid;
final messages = it.pinnedMessages;
return updatePinnedMessages(cid, messages.toList(growable: false));
}).toList(growable: false);
final updateReadsFuture = channelStates.map((it) {
final cid = it.channel.cid;
final reads = it.read?.where((it) => it != null) ?? [];
final updateReadsFuture = cleanedChannelStates.map((it) {
final cid = it.channel!.cid;
final reads = it.read;
return updateReads(cid, reads.toList(growable: false));
}).toList(growable: false);
final updateMembersFuture = channelStates.map((it) {
final cid = it.channel.cid;
final members = it.members.where((it) => it != null);
final updateMembersFuture = cleanedChannelStates.map((it) {
final cid = it.channel!.cid;
final members = it.members;
return updateMembers(cid, members.toList(growable: false));
}).toList(growable: false);
+8 -8
View File
@@ -4,25 +4,25 @@ import 'dart:convert';
class ApiError extends Error {
/// Creates a new ApiError instance using the response body and status code
ApiError(this.body, this.status) : jsonData = _decode(body) {
if (jsonData != null && jsonData.containsKey('code')) {
_code = jsonData['code'];
if (jsonData != null && jsonData!.containsKey('code')) {
_code = jsonData!['code'];
}
}
/// Raw body of the response
final String body;
final String? body;
/// Json parsed body
final Map<String, dynamic> jsonData;
final Map<String, dynamic>? jsonData;
/// Http status code of the response
final int status;
final int? status;
/// Stream specific error code
int get code => _code;
int _code;
int? get code => _code;
int? _code;
static Map<String, dynamic> _decode(String body) {
static Map<String, dynamic>? _decode(String? body) {
try {
if (body == null) {
return null;
@@ -0,0 +1,9 @@
/// Useful extension functions for [Iterable]
extension IterableX<T> on Iterable<T?> {
/// Removes all the null values
/// and converts `Iterable<T?>` into `Iterable<T>`
Iterable<T> get withNullifyer => [
for (final item in this)
if (item != null) item
];
}
@@ -1,6 +1,6 @@
/// Useful extension functions for [Map]
extension MapX on Map {
extension MapX<K, V> on Map<K, V> {
/// Returns a new map with null keys or values removed
Map<String, dynamic> get nullProtected =>
{...this}..removeWhere((key, value) => key == null || value == null);
Map<K, V> get nullProtected =>
Map.from(this)..removeWhere((key, value) => key == null || value == null);
}
@@ -10,7 +10,7 @@ extension RateLimit on Function {
Duration wait, {
bool leading = false,
bool trailing = true,
Duration maxWait,
Duration? maxWait,
}) =>
Debounce(
this,
@@ -40,7 +40,7 @@ Debounce debounce(
Duration wait, {
bool leading = false,
bool trailing = true,
Duration maxWait,
Duration? maxWait,
}) =>
Debounce(
func,
@@ -121,13 +121,13 @@ class Debounce {
Duration wait, {
bool leading = false,
bool trailing = true,
Duration maxWait,
Duration? maxWait,
}) : _leading = leading,
_trailing = trailing,
_wait = wait?.inMilliseconds ?? 0,
_wait = wait.inMilliseconds,
_maxing = maxWait != null {
if (_maxing) {
_maxWait = math.max(maxWait.inMilliseconds, _wait);
_maxWait = math.max(maxWait!.inMilliseconds, _wait);
}
}
@@ -137,15 +137,15 @@ class Debounce {
final int _wait;
final bool _maxing;
int _maxWait;
List<Object> _lastArgs;
Map<Symbol, Object> _lastNamedArgs;
Timer _timer;
int _lastCallTime;
Object _result;
int _lastInvokeTime = 0;
late int _maxWait;
List<Object?>? _lastArgs;
Map<Symbol, Object>? _lastNamedArgs;
Timer? _timer;
int? _lastCallTime;
Object? _result;
int? _lastInvokeTime = 0;
Object _invokeFunc(int time) {
Object? _invokeFunc(int? time) {
final args = _lastArgs;
final namedArgs = _lastNamedArgs;
_lastArgs = _lastNamedArgs = null;
@@ -154,11 +154,11 @@ class Debounce {
}
Timer _startTimer(Function pendingFunc, int wait) =>
Timer(Duration(milliseconds: wait), pendingFunc);
Timer(Duration(milliseconds: wait), pendingFunc as void Function());
bool _shouldInvoke(int time) {
final timeSinceLastCall = time - (_lastCallTime ?? double.nan);
final timeSinceLastInvoke = time - _lastInvokeTime;
final timeSinceLastInvoke = time - _lastInvokeTime!;
// Either this is the first call, activity has stopped and we're at the
// trailing edge, the system time has gone backwards and we're treating
@@ -169,7 +169,7 @@ class Debounce {
(_maxing && timeSinceLastInvoke >= _maxWait);
}
Object _trailingEdge(int time) {
Object? _trailingEdge(int time) {
_timer = null;
// Only invoke if we have `lastArgs` which means `func` has been
@@ -182,8 +182,8 @@ class Debounce {
}
int _remainingWait(int time) {
final timeSinceLastCall = time - _lastCallTime;
final timeSinceLastInvoke = time - _lastInvokeTime;
final timeSinceLastCall = time - _lastCallTime!;
final timeSinceLastInvoke = time - _lastInvokeTime!;
final timeWaiting = _wait - timeSinceLastCall;
return _maxing
@@ -201,7 +201,7 @@ class Debounce {
}
}
Object _leadingEdge(int time) {
Object? _leadingEdge(int? time) {
// Reset any `maxWait` timer.
_lastInvokeTime = time;
// Start the timer for the trailing edge.
@@ -218,7 +218,7 @@ class Debounce {
}
/// Immediately invokes all the remaining delayed functions.
Object flush() {
Object? flush() {
final now = DateTime.now().millisecondsSinceEpoch;
return _timer == null ? _result : _trailingEdge(now);
}
@@ -228,15 +228,15 @@ class Debounce {
/// Calls/invokes this class like a function.
/// Pass [args] and [namedArgs] to be used while invoking [_func].
Object call(
Object? call(
List<dynamic> args, {
Map<Symbol, dynamic> namedArgs,
Map<Symbol, dynamic>? namedArgs,
}) {
final time = DateTime.now().millisecondsSinceEpoch;
final isInvoking = _shouldInvoke(time);
_lastArgs = args;
_lastNamedArgs = namedArgs;
_lastNamedArgs = namedArgs as Map<Symbol, Object>?;
_lastCallTime = time;
if (isInvoking) {
@@ -323,13 +323,13 @@ class Throttle {
void cancel() => _debounce.cancel();
/// Immediately invokes all the remaining delayed functions.
Object flush() => _debounce.flush();
Object? flush() => _debounce.flush();
/// True if there are functions remaining to get invoked.
bool get isPending => _debounce.isPending;
/// Calls/invokes this class like a function.
/// Pass [args] and [namedArgs] to be used while invoking `func`.
Object call(List<dynamic> args, {Map<Symbol, dynamic> namedArgs}) =>
Object? call(List<dynamic> args, {Map<Symbol, dynamic>? namedArgs}) =>
_debounce.call(args, namedArgs: namedArgs);
}
@@ -4,12 +4,15 @@ import 'package:mime/mime.dart';
/// Useful extension functions for [String]
extension StringX on String {
/// Returns the mime type from the passed file name.
http_parser.MediaType get mimeType {
if (this == null) return null;
http_parser.MediaType? get mimeType {
if (toLowerCase().endsWith('heic')) {
return http_parser.MediaType.parse('image/heic');
} else {
return http_parser.MediaType.parse(lookupMimeType(this));
final mimeType = lookupMimeType(this);
if (mimeType == null) {
return null;
}
return http_parser.MediaType.parse(mimeType);
}
}
}
@@ -6,7 +6,13 @@ part 'action.g.dart';
@JsonSerializable()
class Action {
/// Constructor used for json serialization
Action({this.name, this.style, this.text, this.type, this.value});
Action({
required this.name,
this.style = 'default',
required this.text,
required this.type,
this.value,
});
/// Create a new instance from a json
factory Action.fromJson(Map<String, dynamic> json) => _$ActionFromJson(json);
@@ -15,6 +21,7 @@ class Action {
final String name;
/// The style of the action
@JsonKey(defaultValue: 'default')
final String style;
/// The test of the action
@@ -24,7 +31,7 @@ class Action {
final String type;
/// The value of the action
final String value;
final String? value;
/// Serialize to json
Map<String, dynamic> toJson() => _$ActionToJson(this);
@@ -6,13 +6,13 @@ part of 'action.dart';
// JsonSerializableGenerator
// **************************************************************************
Action _$ActionFromJson(Map json) {
Action _$ActionFromJson(Map<String, dynamic> json) {
return Action(
name: json['name'] as String,
style: json['style'] as String,
style: json['style'] as String? ?? 'default',
text: json['text'] as String,
type: json['type'] as String,
value: json['value'] as String,
value: json['value'] as String?,
);
}
@@ -14,10 +14,10 @@ part 'attachment.g.dart';
class Attachment extends Equatable {
/// Constructor used for json serialization
Attachment({
String id,
String? id,
this.type,
this.titleLink,
String title,
String? title,
this.thumbUrl,
this.text,
this.pretext,
@@ -32,17 +32,19 @@ class Attachment extends Equatable {
this.authorLink,
this.authorIcon,
this.assetUrl,
this.actions,
this.extraData,
List<Action>? actions,
this.extraData = const {},
this.file,
UploadState uploadState,
}) : id = id ?? Uuid().v4(),
UploadState? uploadState,
}) : id = id ?? const Uuid().v4(),
title = title ?? file?.name,
localUri = file?.path != null ? Uri.parse(file.path) : null,
uploadState = uploadState ??
((assetUrl != null || imageUrl != null)
? const UploadState.success()
: const UploadState.preparing());
localUri = file?.path != null ? Uri.parse(file!.path!) : null,
actions = actions ?? [] {
this.uploadState = uploadState ??
((assetUrl != null || imageUrl != null)
? const UploadState.success()
: const UploadState.preparing());
}
/// Create a new instance from a json
factory Attachment.fromJson(Map<String, dynamic> json) =>
@@ -56,59 +58,63 @@ class Attachment extends Equatable {
///The attachment type based on the URL resource. This can be: audio,
///image or video
final String type;
final String? type;
///The link to which the attachment message points to.
final String titleLink;
final String? titleLink;
/// The attachment title
final String title;
final String? title;
/// The URL to the attached file thumbnail. You can use this to represent the
/// attached link.
final String thumbUrl;
final String? thumbUrl;
/// The attachment text. It will be displayed in the channel next to the
/// original message.
final String text;
final String? text;
/// Optional text that appears above the attachment block
final String pretext;
final String? pretext;
/// The original URL that was used to scrape this attachment.
final String ogScrapeUrl;
final String? ogScrapeUrl;
/// The URL to the attached image. This is present for URL pointing to an
/// image article (eg. Unsplash)
final String imageUrl;
final String footerIcon;
final String footer;
final String? imageUrl;
final String? footerIcon;
final String? footer;
final dynamic fields;
final String fallback;
final String color;
final String? fallback;
final String? color;
/// The name of the author.
final String authorName;
final String authorLink;
final String authorIcon;
final String? authorName;
final String? authorLink;
final String? authorIcon;
/// The URL to the audio, video or image related to the URL.
final String assetUrl;
final String? assetUrl;
/// Actions from a command
@JsonKey(defaultValue: [])
final List<Action> actions;
final Uri localUri;
final Uri? localUri;
/// The file present inside this attachment.
final AttachmentFile file;
final AttachmentFile? file;
/// The current upload state of the attachment
final UploadState uploadState;
late final UploadState uploadState;
/// Map of custom channel extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
@JsonKey(
includeIfNull: false,
defaultValue: {},
)
final Map<String, Object> extraData;
/// The attachment ID.
///
@@ -147,37 +153,37 @@ class Attachment extends Equatable {
];
/// Serialize to json
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$AttachmentToJson(this), topLevelFields)
..removeWhere((key, value) => dbSpecificTopLevelFields.contains(key));
Map<String, dynamic> toJson() =>
Serialization.moveFromExtraDataToRoot(_$AttachmentToJson(this))
..removeWhere((key, value) => dbSpecificTopLevelFields.contains(key));
/// Serialize to db data
Map<String, dynamic> toData() => Serialization.moveFromExtraDataToRoot(
_$AttachmentToJson(this), topLevelFields + dbSpecificTopLevelFields);
Map<String, dynamic> toData() =>
Serialization.moveFromExtraDataToRoot(_$AttachmentToJson(this));
Attachment copyWith({
String id,
String type,
String titleLink,
String title,
String thumbUrl,
String text,
String pretext,
String ogScrapeUrl,
String imageUrl,
String footerIcon,
String footer,
String? id,
String? type,
String? titleLink,
String? title,
String? thumbUrl,
String? text,
String? pretext,
String? ogScrapeUrl,
String? imageUrl,
String? footerIcon,
String? footer,
dynamic fields,
String fallback,
String color,
String authorName,
String authorLink,
String authorIcon,
String assetUrl,
List<Action> actions,
AttachmentFile file,
UploadState uploadState,
Map<String, dynamic> extraData,
String? fallback,
String? color,
String? authorName,
String? authorLink,
String? authorIcon,
String? assetUrl,
List<Action>? actions,
AttachmentFile? file,
UploadState? uploadState,
Map<String, Object>? extraData,
}) =>
Attachment(
id: id ?? this.id,
@@ -205,7 +211,7 @@ class Attachment extends Equatable {
);
@override
List<Object> get props => [
List<Object?> get props => [
id,
type,
titleLink,
@@ -6,46 +6,40 @@ part of 'attachment.dart';
// JsonSerializableGenerator
// **************************************************************************
Attachment _$AttachmentFromJson(Map json) {
Attachment _$AttachmentFromJson(Map<String, dynamic> json) {
return Attachment(
id: json['id'] as String,
type: json['type'] as String,
titleLink: json['title_link'] as String,
title: json['title'] as String,
thumbUrl: json['thumb_url'] as String,
text: json['text'] as String,
pretext: json['pretext'] as String,
ogScrapeUrl: json['og_scrape_url'] as String,
imageUrl: json['image_url'] as String,
footerIcon: json['footer_icon'] as String,
footer: json['footer'] as String,
id: json['id'] as String?,
type: json['type'] as String?,
titleLink: json['title_link'] as String?,
title: json['title'] as String?,
thumbUrl: json['thumb_url'] as String?,
text: json['text'] as String?,
pretext: json['pretext'] as String?,
ogScrapeUrl: json['og_scrape_url'] as String?,
imageUrl: json['image_url'] as String?,
footerIcon: json['footer_icon'] as String?,
footer: json['footer'] as String?,
fields: json['fields'],
fallback: json['fallback'] as String,
color: json['color'] as String,
authorName: json['author_name'] as String,
authorLink: json['author_link'] as String,
authorIcon: json['author_icon'] as String,
assetUrl: json['asset_url'] as String,
actions: (json['actions'] as List)
?.map((e) => e == null
? null
: Action.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
),
fallback: json['fallback'] as String?,
color: json['color'] as String?,
authorName: json['author_name'] as String?,
authorLink: json['author_link'] as String?,
authorIcon: json['author_icon'] as String?,
assetUrl: json['asset_url'] as String?,
actions: (json['actions'] as List<dynamic>?)
?.map((e) => Action.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
extraData: (json['extra_data'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as Object),
) ??
{},
file: json['file'] == null
? null
: AttachmentFile.fromJson((json['file'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
: AttachmentFile.fromJson(json['file'] as Map<String, dynamic>),
uploadState: json['upload_state'] == null
? null
: UploadState.fromJson((json['upload_state'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
: UploadState.fromJson(json['upload_state'] as Map<String, dynamic>),
);
}
@@ -75,10 +69,10 @@ Map<String, dynamic> _$AttachmentToJson(Attachment instance) {
writeNotNull('author_link', instance.authorLink);
writeNotNull('author_icon', instance.authorIcon);
writeNotNull('asset_url', instance.assetUrl);
writeNotNull('actions', instance.actions?.map((e) => e?.toJson())?.toList());
val['actions'] = instance.actions.map((e) => e.toJson()).toList();
writeNotNull('file', instance.file?.toJson());
writeNotNull('upload_state', instance.uploadState?.toJson());
writeNotNull('extra_data', instance.extraData);
writeNotNull('id', instance.id);
val['upload_state'] = instance.uploadState.toJson();
val['extra_data'] = instance.extraData;
val['id'] = instance.id;
return val;
}
@@ -8,18 +8,21 @@ part 'attachment_file.g.dart';
/// Union class to hold various [UploadState] of a attachment.
@freezed
abstract class UploadState with _$UploadState {
class UploadState with _$UploadState {
/// Preparing state of the union
const factory UploadState.preparing() = Preparing;
/// InProgress state of the union
const factory UploadState.inProgress({int uploaded, int total}) = InProgress;
const factory UploadState.inProgress({
required int uploaded,
required int total,
}) = InProgress;
/// Success state of the union
const factory UploadState.success() = Success;
/// Failed state of the union
const factory UploadState.failed({@required String error}) = Failed;
const factory UploadState.failed({required String error}) = Failed;
/// Creates a new instance from a json
factory UploadState.fromJson(Map<String, dynamic> json) =>
@@ -27,7 +30,7 @@ abstract class UploadState with _$UploadState {
}
/// Helper extension for UploadState
extension UploadStateX on UploadState {
extension UploadStateX on UploadState? {
/// Returns true if state is [Preparing]
bool get isPreparing => this is Preparing;
@@ -41,20 +44,29 @@ extension UploadStateX on UploadState {
bool get isFailed => this is Failed;
}
Uint8List _fromString(String bytes) => Uint8List.fromList(bytes.codeUnits);
Uint8List? _fromString(String? bytes) {
if (bytes == null) return null;
return Uint8List.fromList(bytes.codeUnits);
}
String _toString(Uint8List bytes) => String.fromCharCodes(bytes);
String? _toString(Uint8List? bytes) {
if (bytes == null) return null;
return String.fromCharCodes(bytes);
}
/// The class that contains the information about an attachment file
@JsonSerializable()
class AttachmentFile {
/// Creates a new [AttachmentFile] instance.
const AttachmentFile({
required this.size,
this.path,
this.name,
this.bytes,
this.size,
});
}) : assert(
path != null || bytes != null,
'Either path or bytes should be != null',
);
/// Create a new instance from a json
factory AttachmentFile.fromJson(Map<String, dynamic> json) =>
@@ -65,21 +77,21 @@ class AttachmentFile {
/// ```
/// final File myFile = File(platformFile.path);
/// ```
final String path;
final String? path;
/// File name including its extension.
final String name;
final String? name;
/// Byte data for this file. Particularly useful if you want to manipulate
/// its data or easily upload to somewhere else.
@JsonKey(toJson: _toString, fromJson: _fromString)
final Uint8List bytes;
final Uint8List? bytes;
/// The file size in bytes.
final int size;
final int? size;
/// File extension for this file.
String get extension => name?.split('.')?.last;
String? get extension => name?.split('.').last;
/// Serialize to json
Map<String, dynamic> toJson() => _$AttachmentFileToJson(this);
@@ -1,5 +1,5 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: 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
// 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
part of 'attachment_file.dart';
@@ -8,6 +8,10 @@ part of 'attachment_file.dart';
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
UploadState _$UploadStateFromJson(Map<String, dynamic> json) {
switch (json['runtimeType'] as String) {
case 'preparing':
@@ -28,74 +32,72 @@ UploadState _$UploadStateFromJson(Map<String, dynamic> json) {
class _$UploadStateTearOff {
const _$UploadStateTearOff();
// ignore: unused_element
Preparing preparing() {
return const Preparing();
}
// ignore: unused_element
InProgress inProgress({int uploaded, int total}) {
InProgress inProgress({required int uploaded, required int total}) {
return InProgress(
uploaded: uploaded,
total: total,
);
}
// ignore: unused_element
Success success() {
return const Success();
}
// ignore: unused_element
Failed failed({@required String error}) {
Failed failed({required String error}) {
return Failed(
error: error,
);
}
// ignore: unused_element
UploadState fromJson(Map<String, Object> json) {
return UploadState.fromJson(json);
}
}
/// @nodoc
// ignore: unused_element
const $UploadState = _$UploadStateTearOff();
/// @nodoc
mixin _$UploadState {
@optionalTypeArgs
TResult when<TResult extends Object>({
@required TResult preparing(),
@required TResult inProgress(int uploaded, int total),
@required TResult success(),
@required TResult failed(String error),
});
TResult when<TResult extends Object?>({
required TResult Function() preparing,
required TResult Function(int uploaded, int total) inProgress,
required TResult Function() success,
required TResult Function(String error) failed,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object>({
TResult preparing(),
TResult inProgress(int uploaded, int total),
TResult success(),
TResult failed(String error),
@required TResult orElse(),
});
TResult maybeWhen<TResult extends Object?>({
TResult Function()? preparing,
TResult Function(int uploaded, int total)? inProgress,
TResult Function()? success,
TResult Function(String error)? failed,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object>({
@required TResult preparing(Preparing value),
@required TResult inProgress(InProgress value),
@required TResult success(Success value),
@required TResult failed(Failed value),
});
TResult map<TResult extends Object?>({
required TResult Function(Preparing value) preparing,
required TResult Function(InProgress value) inProgress,
required TResult Function(Success value) success,
required TResult Function(Failed value) failed,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object>({
TResult preparing(Preparing value),
TResult inProgress(InProgress value),
TResult success(Success value),
TResult failed(Failed value),
@required TResult orElse(),
});
Map<String, dynamic> toJson();
TResult maybeMap<TResult extends Object?>({
TResult Function(Preparing value)? preparing,
TResult Function(InProgress value)? inProgress,
TResult Function(Success value)? success,
TResult Function(Failed value)? failed,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
}
/// @nodoc
@@ -154,29 +156,24 @@ class _$Preparing implements Preparing {
@override
@optionalTypeArgs
TResult when<TResult extends Object>({
@required TResult preparing(),
@required TResult inProgress(int uploaded, int total),
@required TResult success(),
@required TResult failed(String error),
TResult when<TResult extends Object?>({
required TResult Function() preparing,
required TResult Function(int uploaded, int total) inProgress,
required TResult Function() success,
required TResult Function(String error) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return preparing();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object>({
TResult preparing(),
TResult inProgress(int uploaded, int total),
TResult success(),
TResult failed(String error),
@required TResult orElse(),
TResult maybeWhen<TResult extends Object?>({
TResult Function()? preparing,
TResult Function(int uploaded, int total)? inProgress,
TResult Function()? success,
TResult Function(String error)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (preparing != null) {
return preparing();
}
@@ -185,29 +182,24 @@ class _$Preparing implements Preparing {
@override
@optionalTypeArgs
TResult map<TResult extends Object>({
@required TResult preparing(Preparing value),
@required TResult inProgress(InProgress value),
@required TResult success(Success value),
@required TResult failed(Failed value),
TResult map<TResult extends Object?>({
required TResult Function(Preparing value) preparing,
required TResult Function(InProgress value) inProgress,
required TResult Function(Success value) success,
required TResult Function(Failed value) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return preparing(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object>({
TResult preparing(Preparing value),
TResult inProgress(InProgress value),
TResult success(Success value),
TResult failed(Failed value),
@required TResult orElse(),
TResult maybeMap<TResult extends Object?>({
TResult Function(Preparing value)? preparing,
TResult Function(InProgress value)? inProgress,
TResult Function(Success value)? success,
TResult Function(Failed value)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (preparing != null) {
return preparing(this);
}
@@ -245,12 +237,18 @@ class _$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
@override
$Res call({
Object uploaded = freezed,
Object total = freezed,
Object? uploaded = freezed,
Object? total = freezed,
}) {
return _then(InProgress(
uploaded: uploaded == freezed ? _value.uploaded : uploaded as int,
total: total == freezed ? _value.total : total as int,
uploaded: uploaded == freezed
? _value.uploaded
: uploaded // ignore: cast_nullable_to_non_nullable
as int,
total: total == freezed
? _value.total
: total // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
@@ -259,7 +257,7 @@ class _$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
/// @nodoc
class _$InProgress implements InProgress {
const _$InProgress({this.uploaded, this.total});
const _$InProgress({required this.uploaded, required this.total});
factory _$InProgress.fromJson(Map<String, dynamic> json) =>
_$_$InProgressFromJson(json);
@@ -298,29 +296,24 @@ class _$InProgress implements InProgress {
@override
@optionalTypeArgs
TResult when<TResult extends Object>({
@required TResult preparing(),
@required TResult inProgress(int uploaded, int total),
@required TResult success(),
@required TResult failed(String error),
TResult when<TResult extends Object?>({
required TResult Function() preparing,
required TResult Function(int uploaded, int total) inProgress,
required TResult Function() success,
required TResult Function(String error) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return inProgress(uploaded, total);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object>({
TResult preparing(),
TResult inProgress(int uploaded, int total),
TResult success(),
TResult failed(String error),
@required TResult orElse(),
TResult maybeWhen<TResult extends Object?>({
TResult Function()? preparing,
TResult Function(int uploaded, int total)? inProgress,
TResult Function()? success,
TResult Function(String error)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (inProgress != null) {
return inProgress(uploaded, total);
}
@@ -329,29 +322,24 @@ class _$InProgress implements InProgress {
@override
@optionalTypeArgs
TResult map<TResult extends Object>({
@required TResult preparing(Preparing value),
@required TResult inProgress(InProgress value),
@required TResult success(Success value),
@required TResult failed(Failed value),
TResult map<TResult extends Object?>({
required TResult Function(Preparing value) preparing,
required TResult Function(InProgress value) inProgress,
required TResult Function(Success value) success,
required TResult Function(Failed value) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return inProgress(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object>({
TResult preparing(Preparing value),
TResult inProgress(InProgress value),
TResult success(Success value),
TResult failed(Failed value),
@required TResult orElse(),
TResult maybeMap<TResult extends Object?>({
TResult Function(Preparing value)? preparing,
TResult Function(InProgress value)? inProgress,
TResult Function(Success value)? success,
TResult Function(Failed value)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (inProgress != null) {
return inProgress(this);
}
@@ -365,15 +353,17 @@ class _$InProgress implements InProgress {
}
abstract class InProgress implements UploadState {
const factory InProgress({int uploaded, int total}) = _$InProgress;
const factory InProgress({required int uploaded, required int total}) =
_$InProgress;
factory InProgress.fromJson(Map<String, dynamic> json) =
_$InProgress.fromJson;
int get uploaded;
int get total;
int get uploaded => throw _privateConstructorUsedError;
int get total => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$InProgressCopyWith<InProgress> get copyWith;
$InProgressCopyWith<InProgress> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
@@ -416,29 +406,24 @@ class _$Success implements Success {
@override
@optionalTypeArgs
TResult when<TResult extends Object>({
@required TResult preparing(),
@required TResult inProgress(int uploaded, int total),
@required TResult success(),
@required TResult failed(String error),
TResult when<TResult extends Object?>({
required TResult Function() preparing,
required TResult Function(int uploaded, int total) inProgress,
required TResult Function() success,
required TResult Function(String error) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return success();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object>({
TResult preparing(),
TResult inProgress(int uploaded, int total),
TResult success(),
TResult failed(String error),
@required TResult orElse(),
TResult maybeWhen<TResult extends Object?>({
TResult Function()? preparing,
TResult Function(int uploaded, int total)? inProgress,
TResult Function()? success,
TResult Function(String error)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (success != null) {
return success();
}
@@ -447,29 +432,24 @@ class _$Success implements Success {
@override
@optionalTypeArgs
TResult map<TResult extends Object>({
@required TResult preparing(Preparing value),
@required TResult inProgress(InProgress value),
@required TResult success(Success value),
@required TResult failed(Failed value),
TResult map<TResult extends Object?>({
required TResult Function(Preparing value) preparing,
required TResult Function(InProgress value) inProgress,
required TResult Function(Success value) success,
required TResult Function(Failed value) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return success(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object>({
TResult preparing(Preparing value),
TResult inProgress(InProgress value),
TResult success(Success value),
TResult failed(Failed value),
@required TResult orElse(),
TResult maybeMap<TResult extends Object?>({
TResult Function(Preparing value)? preparing,
TResult Function(InProgress value)? inProgress,
TResult Function(Success value)? success,
TResult Function(Failed value)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (success != null) {
return success(this);
}
@@ -506,10 +486,13 @@ class _$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
@override
$Res call({
Object error = freezed,
Object? error = freezed,
}) {
return _then(Failed(
error: error == freezed ? _value.error : error as String,
error: error == freezed
? _value.error
: error // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
@@ -518,7 +501,7 @@ class _$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
/// @nodoc
class _$Failed implements Failed {
const _$Failed({@required this.error}) : assert(error != null);
const _$Failed({required this.error});
factory _$Failed.fromJson(Map<String, dynamic> json) =>
_$_$FailedFromJson(json);
@@ -550,29 +533,24 @@ class _$Failed implements Failed {
@override
@optionalTypeArgs
TResult when<TResult extends Object>({
@required TResult preparing(),
@required TResult inProgress(int uploaded, int total),
@required TResult success(),
@required TResult failed(String error),
TResult when<TResult extends Object?>({
required TResult Function() preparing,
required TResult Function(int uploaded, int total) inProgress,
required TResult Function() success,
required TResult Function(String error) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return failed(error);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object>({
TResult preparing(),
TResult inProgress(int uploaded, int total),
TResult success(),
TResult failed(String error),
@required TResult orElse(),
TResult maybeWhen<TResult extends Object?>({
TResult Function()? preparing,
TResult Function(int uploaded, int total)? inProgress,
TResult Function()? success,
TResult Function(String error)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (failed != null) {
return failed(error);
}
@@ -581,29 +559,24 @@ class _$Failed implements Failed {
@override
@optionalTypeArgs
TResult map<TResult extends Object>({
@required TResult preparing(Preparing value),
@required TResult inProgress(InProgress value),
@required TResult success(Success value),
@required TResult failed(Failed value),
TResult map<TResult extends Object?>({
required TResult Function(Preparing value) preparing,
required TResult Function(InProgress value) inProgress,
required TResult Function(Success value) success,
required TResult Function(Failed value) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return failed(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object>({
TResult preparing(Preparing value),
TResult inProgress(InProgress value),
TResult success(Success value),
TResult failed(Failed value),
@required TResult orElse(),
TResult maybeMap<TResult extends Object?>({
TResult Function(Preparing value)? preparing,
TResult Function(InProgress value)? inProgress,
TResult Function(Success value)? success,
TResult Function(Failed value)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (failed != null) {
return failed(this);
}
@@ -617,11 +590,11 @@ class _$Failed implements Failed {
}
abstract class Failed implements UploadState {
const factory Failed({@required String error}) = _$Failed;
const factory Failed({required String error}) = _$Failed;
factory Failed.fromJson(Map<String, dynamic> json) = _$Failed.fromJson;
String get error;
String get error => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$FailedCopyWith<Failed> get copyWith;
$FailedCopyWith<Failed> get copyWith => throw _privateConstructorUsedError;
}
@@ -6,12 +6,12 @@ part of 'attachment_file.dart';
// JsonSerializableGenerator
// **************************************************************************
AttachmentFile _$AttachmentFileFromJson(Map json) {
AttachmentFile _$AttachmentFileFromJson(Map<String, dynamic> json) {
return AttachmentFile(
path: json['path'] as String,
name: json['name'] as String,
bytes: _fromString(json['bytes'] as String),
size: json['size'] as int,
path: json['path'] as String?,
name: json['name'] as String?,
bytes: _fromString(json['bytes'] as String?),
size: json['size'] as int?,
);
}
@@ -23,14 +23,14 @@ Map<String, dynamic> _$AttachmentFileToJson(AttachmentFile instance) =>
'size': instance.size,
};
_$Preparing _$_$PreparingFromJson(Map json) {
_$Preparing _$_$PreparingFromJson(Map<String, dynamic> json) {
return _$Preparing();
}
Map<String, dynamic> _$_$PreparingToJson(_$Preparing instance) =>
<String, dynamic>{};
_$InProgress _$_$InProgressFromJson(Map json) {
_$InProgress _$_$InProgressFromJson(Map<String, dynamic> json) {
return _$InProgress(
uploaded: json['uploaded'] as int,
total: json['total'] as int,
@@ -43,14 +43,14 @@ Map<String, dynamic> _$_$InProgressToJson(_$InProgress instance) =>
'total': instance.total,
};
_$Success _$_$SuccessFromJson(Map json) {
_$Success _$_$SuccessFromJson(Map<String, dynamic> json) {
return _$Success();
}
Map<String, dynamic> _$_$SuccessToJson(_$Success instance) =>
<String, dynamic>{};
_$Failed _$_$FailedFromJson(Map json) {
_$Failed _$_$FailedFromJson(Map<String, dynamic> json) {
return _$Failed(
error: json['error'] as String,
);
@@ -1,5 +1,6 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:stream_chat/src/models/command.dart';
part 'channel_config.g.dart';
/// The class that contains the information about the configuration of a channel
@@ -7,35 +8,38 @@ part 'channel_config.g.dart';
class ChannelConfig {
/// Constructor used for json serialization
ChannelConfig({
this.automod,
this.commands,
this.connectEvents,
this.createdAt,
this.updatedAt,
this.maxMessageLength,
this.messageRetention,
this.mutes,
this.name,
this.reactions,
this.readEvents,
this.replies,
this.search,
this.typingEvents,
this.uploads,
this.urlEnrichment,
});
this.automod = 'flag',
this.commands = const [],
this.connectEvents = false,
DateTime? createdAt,
DateTime? updatedAt,
this.maxMessageLength = 0,
this.messageRetention = '',
this.mutes = false,
this.reactions = false,
this.readEvents = false,
this.replies = false,
this.search = false,
this.typingEvents = false,
this.uploads = false,
this.urlEnrichment = false,
}) : createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now();
/// Create a new instance from a json
factory ChannelConfig.fromJson(Map<String, dynamic> json) =>
_$ChannelConfigFromJson(json);
/// Moderation configuration
@JsonKey(defaultValue: 'flag')
final String automod;
/// List of available commands
@JsonKey(defaultValue: [])
final List<Command> commands;
/// True if the channel should send connect events
@JsonKey(defaultValue: false)
final bool connectEvents;
/// Date of channel creation
@@ -45,36 +49,43 @@ class ChannelConfig {
final DateTime updatedAt;
/// Max channel message length
@JsonKey(defaultValue: 0)
final int maxMessageLength;
/// Duration of message retention
@JsonKey(defaultValue: '')
final String messageRetention;
/// True if users can be muted
@JsonKey(defaultValue: false)
final bool mutes;
/// Name of the channel
final String name;
/// True if reaction are active for this channel
@JsonKey(defaultValue: false)
final bool reactions;
/// True if readEvents are active for this channel
@JsonKey(defaultValue: false)
final bool readEvents;
/// True if reply message are active for this channel
@JsonKey(defaultValue: false)
final bool replies;
/// True if it's possible to perform a search in this channel
@JsonKey(defaultValue: false)
final bool search;
/// True if typing events should be sent for this channel
@JsonKey(defaultValue: false)
final bool typingEvents;
/// True if it's possible to upload files to this channel
@JsonKey(defaultValue: false)
final bool uploads;
/// True if urls appears as attachments
@JsonKey(defaultValue: false)
final bool urlEnrichment;
/// Serialize to json
@@ -6,48 +6,43 @@ part of 'channel_config.dart';
// JsonSerializableGenerator
// **************************************************************************
ChannelConfig _$ChannelConfigFromJson(Map json) {
ChannelConfig _$ChannelConfigFromJson(Map<String, dynamic> json) {
return ChannelConfig(
automod: json['automod'] as String,
commands: (json['commands'] as List)
?.map((e) => e == null
? null
: Command.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
connectEvents: json['connect_events'] as bool,
automod: json['automod'] as String? ?? 'flag',
commands: (json['commands'] as List<dynamic>?)
?.map((e) => Command.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
connectEvents: json['connect_events'] as bool? ?? false,
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
updatedAt: json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String),
maxMessageLength: json['max_message_length'] as int,
messageRetention: json['message_retention'] as String,
mutes: json['mutes'] as bool,
name: json['name'] as String,
reactions: json['reactions'] as bool,
readEvents: json['read_events'] as bool,
replies: json['replies'] as bool,
search: json['search'] as bool,
typingEvents: json['typing_events'] as bool,
uploads: json['uploads'] as bool,
urlEnrichment: json['url_enrichment'] as bool,
maxMessageLength: json['max_message_length'] as int? ?? 0,
messageRetention: json['message_retention'] as String? ?? '',
mutes: json['mutes'] as bool? ?? false,
reactions: json['reactions'] as bool? ?? false,
readEvents: json['read_events'] as bool? ?? false,
replies: json['replies'] as bool? ?? false,
search: json['search'] as bool? ?? false,
typingEvents: json['typing_events'] as bool? ?? false,
uploads: json['uploads'] as bool? ?? false,
urlEnrichment: json['url_enrichment'] as bool? ?? false,
);
}
Map<String, dynamic> _$ChannelConfigToJson(ChannelConfig instance) =>
<String, dynamic>{
'automod': instance.automod,
'commands': instance.commands?.map((e) => e?.toJson())?.toList(),
'commands': instance.commands.map((e) => e.toJson()).toList(),
'connect_events': instance.connectEvents,
'created_at': instance.createdAt?.toIso8601String(),
'updated_at': instance.updatedAt?.toIso8601String(),
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'max_message_length': instance.maxMessageLength,
'message_retention': instance.messageRetention,
'mutes': instance.mutes,
'name': instance.name,
'reactions': instance.reactions,
'read_events': instance.readEvents,
'replies': instance.replies,
@@ -10,20 +10,29 @@ part 'channel_model.g.dart';
class ChannelModel {
/// Constructor used for json serialization
ChannelModel({
this.id,
this.type,
this.cid,
this.config,
String? id,
String? type,
String? cid,
ChannelConfig? config,
this.createdBy,
this.frozen,
this.frozen = false,
this.lastMessageAt,
this.createdAt,
this.updatedAt,
DateTime? createdAt,
DateTime? updatedAt,
this.deletedAt,
this.memberCount,
this.extraData,
this.memberCount = 0,
this.extraData = const {},
this.team,
});
}) : assert(
(cid != null && cid.contains(':')) || (id != null && type != null),
'provide either a cid or an id and type',
),
id = id ?? cid!.split(':')[1],
type = type ?? cid!.split(':')[0],
cid = cid ?? '$type:$id',
config = config ?? ChannelConfig(),
createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now();
/// Create a new instance from a json
factory ChannelModel.fromJson(Map<String, dynamic> json) =>
@@ -46,15 +55,15 @@ class ChannelModel {
/// The user that created this channel
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final User createdBy;
final User? createdBy;
/// True if this channel is frozen
@JsonKey(includeIfNull: false)
@JsonKey(includeIfNull: false, defaultValue: false)
final bool frozen;
/// The date of the last message
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime lastMessageAt;
final DateTime? lastMessageAt;
/// The date of channel creation
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@@ -66,19 +75,23 @@ class ChannelModel {
/// The date of channel deletion
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime deletedAt;
final DateTime? deletedAt;
/// The count of this channel members
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false, toJson: Serialization.readOnly, defaultValue: 0)
final int memberCount;
/// Map of custom channel extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
@JsonKey(
includeIfNull: false,
defaultValue: {},
)
final Map<String, Object> extraData;
/// The team the channel belongs to
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String team;
final String? team;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -99,29 +112,28 @@ class ChannelModel {
/// Shortcut for channel name
String get name =>
extraData?.containsKey('name') == true ? extraData['name'] : cid;
extraData.containsKey('name') ? extraData['name'] as String : cid;
/// Serialize to json
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$ChannelModelToJson(this),
topLevelFields,
);
/// Creates a copy of [ChannelModel] with specified attributes overridden.
ChannelModel copyWith({
String id,
String type,
String cid,
ChannelConfig config,
User createdBy,
bool frozen,
DateTime lastMessageAt,
DateTime createdAt,
DateTime updatedAt,
DateTime deletedAt,
int memberCount,
Map<String, dynamic> extraData,
String team,
String? id,
String? type,
String? cid,
ChannelConfig? config,
User? createdBy,
bool? frozen,
DateTime? lastMessageAt,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? deletedAt,
int? memberCount,
Map<String, Object>? extraData,
String? team,
}) =>
ChannelModel(
id: id ?? this.id,
@@ -141,7 +153,7 @@ class ChannelModel {
/// Returns a new [ChannelModel] that is a combination of this channelModel
/// and the given [other] channelModel.
ChannelModel merge(ChannelModel other) {
ChannelModel merge(ChannelModel? other) {
if (other == null) return this;
return copyWith(
id: other.id,
@@ -6,22 +6,18 @@ part of 'channel_model.dart';
// JsonSerializableGenerator
// **************************************************************************
ChannelModel _$ChannelModelFromJson(Map json) {
ChannelModel _$ChannelModelFromJson(Map<String, dynamic> json) {
return ChannelModel(
id: json['id'] as String,
type: json['type'] as String,
cid: json['cid'] as String,
id: json['id'] as String?,
type: json['type'] as String?,
cid: json['cid'] as String?,
config: json['config'] == null
? null
: ChannelConfig.fromJson((json['config'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
: ChannelConfig.fromJson(json['config'] as Map<String, dynamic>),
createdBy: json['created_by'] == null
? null
: User.fromJson((json['created_by'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
frozen: json['frozen'] as bool,
: User.fromJson(json['created_by'] as Map<String, dynamic>),
frozen: json['frozen'] as bool? ?? false,
lastMessageAt: json['last_message_at'] == null
? null
: DateTime.parse(json['last_message_at'] as String),
@@ -34,11 +30,12 @@ ChannelModel _$ChannelModelFromJson(Map json) {
deletedAt: json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
memberCount: json['member_count'] as int,
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
),
team: json['team'] as String,
memberCount: json['member_count'] as int? ?? 0,
extraData: (json['extra_data'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as Object),
) ??
{},
team: json['team'] as String?,
);
}
@@ -57,13 +54,13 @@ Map<String, dynamic> _$ChannelModelToJson(ChannelModel instance) {
writeNotNull('cid', readonly(instance.cid));
writeNotNull('config', readonly(instance.config));
writeNotNull('created_by', readonly(instance.createdBy));
writeNotNull('frozen', instance.frozen);
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));
writeNotNull('extra_data', instance.extraData);
val['extra_data'] = instance.extraData;
writeNotNull('team', readonly(instance.team));
return val;
}
@@ -22,24 +22,29 @@ class ChannelState {
});
/// The channel to which this state belongs
final ChannelModel channel;
final ChannelModel? channel;
/// A paginated list of channel messages
@JsonKey(defaultValue: <Message>[])
final List<Message> messages;
/// A paginated list of channel members
@JsonKey(defaultValue: <Member>[])
final List<Member> members;
/// A paginated list of pinned messages
@JsonKey(defaultValue: <Message>[])
final List<Message> pinnedMessages;
/// The count of users watching the channel
final int watcherCount;
final int? watcherCount;
/// A paginated list of users watching the channel
@JsonKey(defaultValue: <User>[])
final List<User> watchers;
/// The list of channel reads
@JsonKey(defaultValue: <Read>[])
final List<Read> read;
/// Create a new instance from a json
@@ -51,13 +56,13 @@ class ChannelState {
/// Creates a copy of [ChannelState] with specified attributes overridden.
ChannelState copyWith({
ChannelModel channel,
List<Message> messages,
List<Member> members,
List<Message> pinnedMessages,
int watcherCount,
List<User> watchers,
List<Read> read,
ChannelModel? channel,
List<Message>? messages,
List<Member>? members,
List<Message>? pinnedMessages,
int? watcherCount,
List<User>? watchers,
List<Read>? read,
}) =>
ChannelState(
channel: channel ?? this.channel,
@@ -6,60 +6,43 @@ part of 'channel_state.dart';
// JsonSerializableGenerator
// **************************************************************************
ChannelState _$ChannelStateFromJson(Map json) {
ChannelState _$ChannelStateFromJson(Map<String, dynamic> json) {
return ChannelState(
channel: json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
messages: (json['messages'] as List)
?.map((e) => e == null
? null
: Message.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
members: (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
pinnedMessages: (json['pinned_messages'] as List)
?.map((e) => e == null
? null
: Message.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
watcherCount: json['watcher_count'] as int,
watchers: (json['watchers'] as List)
?.map((e) => e == null
? null
: User.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
read: (json['read'] as List)
?.map((e) => e == null
? null
: Read.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
: ChannelModel.fromJson(json['channel'] as Map<String, dynamic>),
messages: (json['messages'] as List<dynamic>?)
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
members: (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
pinnedMessages: (json['pinned_messages'] as List<dynamic>?)
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
watcherCount: json['watcher_count'] as int?,
watchers: (json['watchers'] as List<dynamic>?)
?.map((e) => User.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
read: (json['read'] as List<dynamic>?)
?.map((e) => Read.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
);
}
Map<String, dynamic> _$ChannelStateToJson(ChannelState instance) =>
<String, dynamic>{
'channel': instance.channel?.toJson(),
'messages': instance.messages?.map((e) => e?.toJson())?.toList(),
'members': instance.members?.map((e) => e?.toJson())?.toList(),
'messages': instance.messages.map((e) => e.toJson()).toList(),
'members': instance.members.map((e) => e.toJson()).toList(),
'pinned_messages':
instance.pinnedMessages?.map((e) => e?.toJson())?.toList(),
instance.pinnedMessages.map((e) => e.toJson()).toList(),
'watcher_count': instance.watcherCount,
'watchers': instance.watchers?.map((e) => e?.toJson())?.toList(),
'read': instance.read?.map((e) => e?.toJson())?.toList(),
'watchers': instance.watchers.map((e) => e.toJson()).toList(),
'read': instance.read.map((e) => e.toJson()).toList(),
};
@@ -7,9 +7,9 @@ part 'command.g.dart';
class Command {
/// Constructor used for json serialization
Command({
this.name,
this.description,
this.args,
required this.name,
required this.description,
required this.args,
});
/// Create a new instance from a json
@@ -6,7 +6,7 @@ part of 'command.dart';
// JsonSerializableGenerator
// **************************************************************************
Command _$CommandFromJson(Map json) {
Command _$CommandFromJson(Map<String, dynamic> json) {
return Command(
name: json['name'] as String,
description: json['description'] as String,
@@ -7,8 +7,8 @@ part 'device.g.dart';
class Device {
/// Constructor used for json serialization
Device({
this.id,
this.pushProvider,
required this.id,
required this.pushProvider,
});
/// Create a new instance from a json
@@ -6,7 +6,7 @@ part of 'device.dart';
// JsonSerializableGenerator
// **************************************************************************
Device _$DeviceFromJson(Map json) {
Device _$DeviceFromJson(Map<String, dynamic> json) {
return Device(
id: json['id'] as String,
pushProvider: json['push_provider'] as String,
+56 -57
View File
@@ -10,7 +10,7 @@ part 'event.g.dart';
@JsonSerializable()
class Event {
/// Constructor used for json serialization
Event({
const Event({
this.type,
this.cid,
this.connectionId,
@@ -27,72 +27,73 @@ class Event {
this.channelId,
this.channelType,
this.parentId,
this.extraData,
}) : isLocal = true;
this.extraData = const {},
this.isLocal = true,
});
/// Create a new instance from a json
factory Event.fromJson(Map<String, dynamic> json) =>
_$EventFromJson(Serialization.moveToExtraDataFromRoot(
json,
topLevelFields,
))
..isLocal = false;
));
/// The type of the event
/// [EventType] contains some predefined constant types
final String type;
final String? type;
/// The channel cid to which the event belongs
final String cid;
final String? cid;
/// The channel id to which the event belongs
final String channelId;
final String? channelId;
/// The channel type to which the event belongs
final String channelType;
final String? channelType;
/// The connection id in which the event has been sent
final String connectionId;
final String? connectionId;
/// The date of creation of the event
final DateTime createdAt;
final DateTime? createdAt;
/// User object of the health check user
final OwnUser me;
final OwnUser? me;
/// User object of the current user
final User user;
final User? user;
/// The message sent with the event
final Message message;
final Message? message;
/// The channel sent with the event
final EventChannel channel;
final EventChannel? channel;
/// The member sent with the event
final Member member;
final Member? member;
/// The reaction sent with the event
final Reaction reaction;
final Reaction? reaction;
/// The number of unread messages for current user
final int totalUnreadCount;
final int? totalUnreadCount;
/// User total unread channels
final int unreadChannels;
final int? unreadChannels;
/// Online status
final bool online;
final bool? online;
/// The id of the parent message of a thread
final String parentId;
final String? parentId;
/// True if the event is generated by this client
bool isLocal;
@JsonKey(defaultValue: false)
final bool isLocal;
/// Map of custom channel extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
@JsonKey(defaultValue: {})
final Map<String, Object> extraData;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -119,28 +120,27 @@ class Event {
/// Serialize to json
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$EventToJson(this),
topLevelFields,
);
/// Creates a copy of [Event] with specified attributes overridden.
Event copyWith({
String type,
String cid,
String channelId,
String channelType,
String connectionId,
DateTime createdAt,
OwnUser me,
User user,
Message message,
EventChannel channel,
Member member,
Reaction reaction,
int totalUnreadCount,
int unreadChannels,
bool online,
String parentId,
Map<String, dynamic> extraData,
String? type,
String? cid,
String? channelId,
String? channelType,
String? connectionId,
DateTime? createdAt,
OwnUser? me,
User? user,
Message? message,
EventChannel? channel,
Member? member,
Reaction? reaction,
int? totalUnreadCount,
int? unreadChannels,
bool? online,
String? parentId,
Map<String, Object>? extraData,
}) =>
Event(
type: type ?? this.type,
@@ -169,18 +169,18 @@ class EventChannel extends ChannelModel {
/// Constructor used for json serialization
EventChannel({
this.members,
String id,
String type,
String cid,
ChannelConfig config,
User createdBy,
bool frozen,
DateTime lastMessageAt,
DateTime createdAt,
DateTime updatedAt,
DateTime deletedAt,
int memberCount,
Map<String, dynamic> extraData,
String? id,
String? type,
required String cid,
required ChannelConfig config,
User? createdBy,
bool frozen = false,
DateTime? lastMessageAt,
required DateTime createdAt,
required DateTime updatedAt,
DateTime? deletedAt,
required int memberCount,
Map<String, Object>? extraData,
}) : super(
id: id,
type: type,
@@ -193,7 +193,7 @@ class EventChannel extends ChannelModel {
updatedAt: updatedAt,
deletedAt: deletedAt,
memberCount: memberCount,
extraData: extraData,
extraData: extraData ?? {},
);
/// Create a new instance from a json
@@ -204,7 +204,7 @@ class EventChannel extends ChannelModel {
));
/// A paginated list of channel members
final List<Member> members;
final List<Member>? members;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -217,6 +217,5 @@ class EventChannel extends ChannelModel {
@override
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$EventChannelToJson(this),
topLevelFields,
);
}
@@ -6,126 +6,93 @@ part of 'event.dart';
// JsonSerializableGenerator
// **************************************************************************
Event _$EventFromJson(Map json) {
Event _$EventFromJson(Map<String, dynamic> json) {
return Event(
type: json['type'] as String,
cid: json['cid'] as String,
connectionId: json['connection_id'] as String,
type: json['type'] as String?,
cid: json['cid'] as String?,
connectionId: json['connection_id'] as String?,
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
me: json['me'] == null
? null
: OwnUser.fromJson((json['me'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
: OwnUser.fromJson(json['me'] as Map<String, dynamic>),
user: json['user'] == null
? null
: User.fromJson((json['user'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
: User.fromJson(json['user'] as Map<String, dynamic>),
message: json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
totalUnreadCount: json['total_unread_count'] as int,
unreadChannels: json['unread_channels'] as int,
: Message.fromJson(json['message'] as Map<String, dynamic>),
totalUnreadCount: json['total_unread_count'] as int?,
unreadChannels: json['unread_channels'] as int?,
reaction: json['reaction'] == null
? null
: Reaction.fromJson((json['reaction'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
online: json['online'] as bool,
: Reaction.fromJson(json['reaction'] as Map<String, dynamic>),
online: json['online'] as bool?,
channel: json['channel'] == null
? null
: EventChannel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
: EventChannel.fromJson(json['channel'] as Map<String, dynamic>),
member: json['member'] == null
? null
: Member.fromJson((json['member'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
channelId: json['channel_id'] as String,
channelType: json['channel_type'] as String,
parentId: json['parent_id'] as String,
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
),
)..isLocal = json['is_local'] as bool;
: Member.fromJson(json['member'] as Map<String, dynamic>),
channelId: json['channel_id'] as String?,
channelType: json['channel_type'] as String?,
parentId: json['parent_id'] as String?,
extraData: (json['extra_data'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as Object),
) ??
{},
isLocal: json['is_local'] as bool? ?? false,
);
}
Map<String, dynamic> _$EventToJson(Event instance) {
final val = <String, dynamic>{
'type': instance.type,
'cid': instance.cid,
'channel_id': instance.channelId,
'channel_type': instance.channelType,
'connection_id': instance.connectionId,
'created_at': instance.createdAt?.toIso8601String(),
'me': instance.me?.toJson(),
'user': instance.user?.toJson(),
'message': instance.message?.toJson(),
'channel': instance.channel?.toJson(),
'member': instance.member?.toJson(),
'reaction': instance.reaction?.toJson(),
'total_unread_count': instance.totalUnreadCount,
'unread_channels': instance.unreadChannels,
'online': instance.online,
'parent_id': instance.parentId,
'is_local': instance.isLocal,
};
Map<String, dynamic> _$EventToJson(Event instance) => <String, dynamic>{
'type': instance.type,
'cid': instance.cid,
'channel_id': instance.channelId,
'channel_type': instance.channelType,
'connection_id': instance.connectionId,
'created_at': instance.createdAt?.toIso8601String(),
'me': instance.me?.toJson(),
'user': instance.user?.toJson(),
'message': instance.message?.toJson(),
'channel': instance.channel?.toJson(),
'member': instance.member?.toJson(),
'reaction': instance.reaction?.toJson(),
'total_unread_count': instance.totalUnreadCount,
'unread_channels': instance.unreadChannels,
'online': instance.online,
'parent_id': instance.parentId,
'is_local': instance.isLocal,
'extra_data': instance.extraData,
};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('extra_data', instance.extraData);
return val;
}
EventChannel _$EventChannelFromJson(Map json) {
EventChannel _$EventChannelFromJson(Map<String, dynamic> json) {
return EventChannel(
members: (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
id: json['id'] as String,
type: json['type'] as String,
members: (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList(),
id: json['id'] as String?,
type: json['type'] as String?,
cid: json['cid'] as String,
config: json['config'] == null
? null
: ChannelConfig.fromJson((json['config'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
config: ChannelConfig.fromJson(json['config'] as Map<String, dynamic>),
createdBy: json['created_by'] == null
? null
: User.fromJson((json['created_by'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
frozen: json['frozen'] as bool,
: User.fromJson(json['created_by'] as Map<String, dynamic>),
frozen: json['frozen'] as bool? ?? false,
lastMessageAt: json['last_message_at'] == null
? null
: DateTime.parse(json['last_message_at'] as String),
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
updatedAt: json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String),
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt: json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
memberCount: json['member_count'] as int,
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
),
memberCount: json['member_count'] as int? ?? 0,
extraData: (json['extra_data'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as Object),
) ??
{},
);
}
@@ -144,13 +111,13 @@ Map<String, dynamic> _$EventChannelToJson(EventChannel instance) {
writeNotNull('cid', readonly(instance.cid));
writeNotNull('config', readonly(instance.config));
writeNotNull('created_by', readonly(instance.createdBy));
writeNotNull('frozen', instance.frozen);
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));
writeNotNull('extra_data', instance.extraData);
val['members'] = instance.members?.map((e) => e?.toJson())?.toList();
val['extra_data'] = instance.extraData;
val['members'] = instance.members?.map((e) => e.toJson()).toList();
return val;
}
@@ -0,0 +1,208 @@
// ignore_for_file: non_constant_identifier_names, constant_identifier_names
import 'package:equatable/equatable.dart';
const _groupOperators = [
FilterOperator.and,
FilterOperator.or,
FilterOperator.nor,
];
/// Possible operators to use in filters.
enum FilterOperator {
/// Matches values that are equal to a specified value.
equal,
/// Matches all values that are not equal to a specified value.
notEqual,
/// Matches values that are greater than a specified value.
greater,
/// Matches values that are greater than a specified value.
greaterOrEqual,
/// Matches values that are less than a specified value.
less,
/// Matches values that are less than or equal to a specified value.
lessOrEqual,
/// Matches any of the values specified in an array.
in_,
/// Matches none of the values specified in an array.
notIn,
/// Matches values by performing text search with the specified value.
query,
/// Matches values with the specified prefix.
autoComplete,
/// Matches values that exist/don't exist based on the specified boolean value.
exists,
/// Matches all the values specified in an array.
and,
/// Matches at least one of the values specified in an array.
or,
/// Matches none of the values specified in an array.
nor,
}
/// Helper extension for [FilterOperator]
extension FilterOperatorX on FilterOperator {
/// Converts [FilterOperator] into rew values
String get rawValue => {
FilterOperator.equal: '\$eq',
FilterOperator.notEqual: '\$ne',
FilterOperator.greater: '\$gt',
FilterOperator.greaterOrEqual: '\$gte',
FilterOperator.less: '\$lt',
FilterOperator.lessOrEqual: '\$lte',
FilterOperator.in_: '\$in',
FilterOperator.notIn: '\$nin',
FilterOperator.query: '\$q',
FilterOperator.autoComplete: '\$autocomplete',
FilterOperator.exists: '\$exists',
FilterOperator.and: '\$and',
FilterOperator.or: '\$or',
FilterOperator.nor: '\$nor',
}[this]!;
}
/// Stream supports a limited set of filters for querying channels,
/// users and members. The example below shows how to filter for channels
/// of type messaging where the current user is a member
///
/// ```dart
/// final filter = Filter.and(
/// Filter.equal('type', 'messaging'),
/// Filter.in_('members', [user.id])
/// )
/// ```
/// See <a href="https://getstream.io/chat/docs/query_channels/?language=dart" target="_top">Query Channels Documentation</a>
class Filter extends Equatable {
const Filter.__({
required this.operator,
required this.value,
this.key,
});
Filter._({
required FilterOperator operator,
required this.value,
this.key,
}) : operator = operator.rawValue;
/// Combines the provided filters and matches the values
/// matched by all filters.
factory Filter.and(List<Filter> filters) =>
Filter._(operator: FilterOperator.and, value: filters);
/// Combines the provided filters and matches the values
/// matched by at least one of the filters.
factory Filter.or(List<Filter> filters) =>
Filter._(operator: FilterOperator.or, value: filters);
/// Combines the provided filters and matches the values
/// not matched by all the filters.
factory Filter.nor(List<Filter> filters) =>
Filter._(operator: FilterOperator.nor, value: filters);
/// Matches values that are equal to a specified value.
factory Filter.equal(String key, Object value) =>
Filter._(operator: FilterOperator.equal, key: key, value: value);
/// Matches all values that are not equal to a specified value.
factory Filter.notEqual(String key, Object value) =>
Filter._(operator: FilterOperator.notEqual, key: key, value: value);
/// Matches values that are greater than a specified value.
factory Filter.greater(String key, Object value) =>
Filter._(operator: FilterOperator.greater, key: key, value: value);
/// Matches values that are greater than a specified value.
factory Filter.greaterOrEqual(String key, Object value) =>
Filter._(operator: FilterOperator.greaterOrEqual, key: key, value: value);
/// Matches values that are less than a specified value.
factory Filter.less(String key, Object value) =>
Filter._(operator: FilterOperator.less, key: key, value: value);
/// Matches values that are less than or equal to a specified value.
factory Filter.lessOrEqual(String key, Object value) =>
Filter._(operator: FilterOperator.lessOrEqual, key: key, value: value);
/// Matches any of the values specified in an array.
factory Filter.in_(String key, List<Object> values) =>
Filter._(operator: FilterOperator.in_, key: key, value: values);
/// Matches none of the values specified in an array.
factory Filter.notIn(String key, List<Object> values) =>
Filter._(operator: FilterOperator.notIn, key: key, value: values);
/// Matches values by performing text search with the specified value.
factory Filter.query(String key, String text) =>
Filter._(operator: FilterOperator.query, key: key, value: text);
/// Matches values with the specified prefix.
factory Filter.autoComplete(String key, String text) =>
Filter._(operator: FilterOperator.autoComplete, key: key, value: text);
/// Matches values that exist/don't exist based on the specified boolean value.
factory Filter.exists(String key, {bool exists = true}) =>
Filter._(operator: FilterOperator.exists, key: key, value: exists);
/// Creates a custom [Filter] if there isn't one already available.
const factory Filter.custom({
required String operator,
required Object value,
String? key,
}) = Filter.__;
/// An operator used for the filter. The operator string must start with `$`
final String operator;
/// The "left-hand" side of the filter.
/// Specifies the name of the field the filter should match.
///
/// Some operators like `and` or `or`,
/// don't require the key value to be present.
/// see-more : [_groupOperators]
final String? key;
/// The "right-hand" side of the filter.
/// Specifies the [value] the filter should match.
final Object /*List<Object>|List<Filter>|String*/ value;
@override
List<Object?> get props => [operator, key, value];
/// Serializes to json object
Map<String, Object> toJson() {
final json = <String, Object>{};
final groupOperators = _groupOperators.map((it) => it.rawValue);
assert(
groupOperators.contains(operator) || key != null,
'Filter must contain the `key` when the operator is not a '
'group operator.',
);
if (groupOperators.contains(operator)) {
// Filters with group operators are encoded in the following form:
// { $<operator>: [ <filter 1>, <filter 2> ] }
json[operator] = value;
} else {
// Normal filters are encoded in the following form:
// { key: { $<operator>: <value> } }
json[key!] = {operator: value};
}
return json;
}
}
+28 -23
View File
@@ -12,15 +12,16 @@ class Member {
this.user,
this.inviteAcceptedAt,
this.inviteRejectedAt,
this.invited,
this.invited = false,
this.role,
this.userId,
this.isModerator,
this.createdAt,
this.updatedAt,
this.banned,
this.shadowBanned,
});
this.isModerator = false,
DateTime? createdAt,
DateTime? updatedAt,
this.banned = false,
this.shadowBanned = false,
}) : createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now();
/// Create a new instance from a json
factory Member.fromJson(Map<String, dynamic> json) {
@@ -31,30 +32,34 @@ class Member {
}
/// The interested user
final User user;
final User? user;
/// The date in which the user accepted the invite to the channel
final DateTime inviteAcceptedAt;
final DateTime? inviteAcceptedAt;
/// The date in which the user rejected the invite to the channel
final DateTime inviteRejectedAt;
final DateTime? inviteRejectedAt;
/// True if the user has been invited to the channel
@JsonKey(defaultValue: false)
final bool invited;
/// The role of the user in the channel
final String role;
final String? role;
/// The id of the interested user
final String userId;
final String? userId;
/// True if the user is a moderator of the channel
@JsonKey(defaultValue: false)
final bool isModerator;
/// True if the member is banned from the channel
@JsonKey(defaultValue: false)
final bool banned;
/// True if the member is shadow banned from the channel
@JsonKey(defaultValue: false)
final bool shadowBanned;
/// The date of creation
@@ -65,17 +70,17 @@ class Member {
/// Creates a copy of [Member] with specified attributes overridden.
Member copyWith({
User user,
DateTime inviteAcceptedAt,
DateTime inviteRejectedAt,
bool invited,
String role,
String userId,
bool isModerator,
DateTime createdAt,
DateTime updatedAt,
bool banned,
bool shadowBanned,
User? user,
DateTime? inviteAcceptedAt,
DateTime? inviteRejectedAt,
bool? invited,
String? role,
String? userId,
bool? isModerator,
DateTime? createdAt,
DateTime? updatedAt,
bool? banned,
bool? shadowBanned,
}) =>
Member(
user: user ?? this.user,
@@ -6,31 +6,29 @@ part of 'member.dart';
// JsonSerializableGenerator
// **************************************************************************
Member _$MemberFromJson(Map json) {
Member _$MemberFromJson(Map<String, dynamic> json) {
return Member(
user: json['user'] == null
? null
: User.fromJson((json['user'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
: User.fromJson(json['user'] as Map<String, dynamic>),
inviteAcceptedAt: json['invite_accepted_at'] == null
? null
: DateTime.parse(json['invite_accepted_at'] as String),
inviteRejectedAt: json['invite_rejected_at'] == null
? null
: DateTime.parse(json['invite_rejected_at'] as String),
invited: json['invited'] as bool,
role: json['role'] as String,
userId: json['user_id'] as String,
isModerator: json['is_moderator'] as bool,
invited: json['invited'] as bool? ?? false,
role: json['role'] as String?,
userId: json['user_id'] as String?,
isModerator: json['is_moderator'] as bool? ?? false,
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
updatedAt: json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String),
banned: json['banned'] as bool,
shadowBanned: json['shadow_banned'] as bool,
banned: json['banned'] as bool? ?? false,
shadowBanned: json['shadow_banned'] as bool? ?? false,
);
}
@@ -44,6 +42,6 @@ Map<String, dynamic> _$MemberToJson(Member instance) => <String, dynamic>{
'is_moderator': instance.isModerator,
'banned': instance.banned,
'shadow_banned': instance.shadowBanned,
'created_at': instance.createdAt?.toIso8601String(),
'updated_at': instance.updatedAt?.toIso8601String(),
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
};
+124 -104
View File
@@ -45,13 +45,13 @@ enum MessageSendingStatus {
class Message extends Equatable {
/// Constructor used for json serialization
Message({
String id,
String? id,
this.text,
this.type,
this.attachments,
this.mentionedUsers,
this.silent,
this.shadowed,
this.type = 'regular',
this.attachments = const [],
this.mentionedUsers = const [],
this.silent = false,
this.shadowed = false,
this.reactionCounts,
this.reactionScores,
this.latestReactions,
@@ -63,19 +63,21 @@ class Message extends Equatable {
this.threadParticipants,
this.showInChannel,
this.command,
this.createdAt,
this.updatedAt,
DateTime? createdAt,
DateTime? updatedAt,
this.user,
this.pinned = false,
this.pinnedAt,
DateTime pinExpires,
DateTime? pinExpires,
this.pinnedBy,
this.extraData,
this.extraData = const {},
this.deletedAt,
this.status = MessageSendingStatus.sent,
this.skipPush,
}) : id = id ?? Uuid().v4(),
pinExpires = pinExpires?.toUtc();
this.skipPush = false,
}) : id = id ?? const Uuid().v4(),
pinExpires = pinExpires?.toUtc(),
createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now();
/// Create a new instance from a json
factory Message.fromJson(Map<String, dynamic> json) => _$MessageFromJson(
@@ -86,75 +88,91 @@ class Message extends Equatable {
final String id;
/// The text of this message
final String text;
final String? text;
/// The status of a sending message
@JsonKey(ignore: true)
final MessageSendingStatus status;
/// The message type
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false,
toJson: Serialization.readOnly,
defaultValue: 'regular',
)
final String type;
/// The list of attachments, either provided by the user or generated from a
/// command or as a result of URL scraping.
@JsonKey(includeIfNull: false)
@JsonKey(
includeIfNull: false,
defaultValue: [],
)
final List<Attachment> attachments;
/// The list of user mentioned in the message
@JsonKey(toJson: Serialization.userIds)
@JsonKey(
toJson: Serialization.userIds,
defaultValue: [],
)
final List<User> mentionedUsers;
/// A map describing the count of number of every reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final Map<String, int> reactionCounts;
final Map<String, int>? reactionCounts;
/// A map describing the count of score of every reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final Map<String, int> reactionScores;
final Map<String, int>? reactionScores;
/// The latest reactions to the message created by any user.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<Reaction> latestReactions;
final List<Reaction>? latestReactions;
/// The reactions added to the message by the current user.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<Reaction> ownReactions;
final List<Reaction>? ownReactions;
/// The ID of the parent message, if the message is a thread reply.
final String parentId;
final String? parentId;
/// A quoted reply message
@JsonKey(toJson: Serialization.readOnly)
final Message quotedMessage;
final Message? quotedMessage;
/// The ID of the quoted message, if the message is a quoted reply.
final String quotedMessageId;
final String? quotedMessageId;
/// Reserved field indicating the number of replies for this message.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final int replyCount;
final int? replyCount;
/// Reserved field indicating the thread participants for this message.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<User> threadParticipants;
final List<User>? threadParticipants;
/// Check if this message needs to show in the channel.
final bool showInChannel;
final bool? showInChannel;
/// If true the message is silent
@JsonKey(defaultValue: false)
final bool silent;
/// If true the message will not send a push notification
@JsonKey(defaultValue: false)
final bool skipPush;
/// If true the message is shadowed
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false,
toJson: Serialization.readOnly,
defaultValue: false,
)
final bool shadowed;
/// A used command name.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String command;
final String? command;
/// Reserved field indicating when the message was created.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@@ -166,27 +184,31 @@ class Message extends Equatable {
/// User who sent the message
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final User user;
final User? user;
/// If true the message is pinned
@JsonKey(defaultValue: false)
final bool pinned;
/// Reserved field indicating when the message was pinned
@JsonKey(toJson: Serialization.readOnly)
final DateTime pinnedAt;
final DateTime? pinnedAt;
/// Reserved field indicating when the message will expire
///
/// if `null` message has no expiry
final DateTime pinExpires;
final DateTime? pinExpires;
/// Reserved field indicating who pinned the message
@JsonKey(toJson: Serialization.readOnly)
final User pinnedBy;
final User? pinnedBy;
/// Message custom extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
@JsonKey(
includeIfNull: false,
defaultValue: {},
)
final Map<String, Object> extraData;
/// True if the message is a system info
bool get isSystem => type == 'system';
@@ -199,7 +221,7 @@ class Message extends Equatable {
/// Reserved field indicating when the message was deleted.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime deletedAt;
final DateTime? deletedAt;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -236,39 +258,40 @@ class Message extends Equatable {
/// Serialize to json
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$MessageToJson(this), topLevelFields);
_$MessageToJson(this),
);
/// Creates a copy of [Message] with specified attributes overridden.
Message copyWith({
String id,
String text,
String type,
List<Attachment> attachments,
List<User> mentionedUsers,
Map<String, int> reactionCounts,
Map<String, int> reactionScores,
List<Reaction> latestReactions,
List<Reaction> ownReactions,
String parentId,
Message quotedMessage,
String quotedMessageId,
int replyCount,
List<User> threadParticipants,
bool showInChannel,
bool shadowed,
bool silent,
String command,
DateTime createdAt,
DateTime updatedAt,
DateTime deletedAt,
User user,
bool pinned,
DateTime pinnedAt,
Object pinExpires = _pinExpires,
User pinnedBy,
Map<String, dynamic> extraData,
MessageSendingStatus status,
bool skipPush,
String? id,
String? text,
String? type,
List<Attachment>? attachments,
List<User>? mentionedUsers,
Map<String, int>? reactionCounts,
Map<String, int>? reactionScores,
List<Reaction>? latestReactions,
List<Reaction>? ownReactions,
String? parentId,
Message? quotedMessage,
String? quotedMessageId,
int? replyCount,
List<User>? threadParticipants,
bool? showInChannel,
bool? shadowed,
bool? silent,
String? command,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? deletedAt,
User? user,
bool? pinned,
DateTime? pinnedAt,
Object? pinExpires = _pinExpires,
User? pinnedBy,
Map<String, Object>? extraData,
MessageSendingStatus? status,
bool? skipPush,
}) {
assert(() {
if (pinExpires is! DateTime &&
@@ -306,49 +329,47 @@ class Message extends Equatable {
pinned: pinned ?? this.pinned,
pinnedAt: pinnedAt ?? this.pinnedAt,
pinnedBy: pinnedBy ?? this.pinnedBy,
pinExpires: pinExpires == _pinExpires ? this.pinExpires : pinExpires,
pinExpires:
pinExpires == _pinExpires ? this.pinExpires : pinExpires as DateTime?,
skipPush: skipPush ?? this.skipPush,
);
}
/// Returns a new [Message] that is a combination of this message and the
/// given [other] message.
Message merge(Message other) {
if (other == null) return this;
return copyWith(
id: other.id,
text: other.text,
type: other.type,
attachments: other.attachments,
mentionedUsers: other.mentionedUsers,
reactionCounts: other.reactionCounts,
reactionScores: other.reactionScores,
latestReactions: other.latestReactions,
ownReactions: other.ownReactions,
parentId: other.parentId,
quotedMessage: other.quotedMessage,
quotedMessageId: other.quotedMessageId,
replyCount: other.replyCount,
threadParticipants: other.threadParticipants,
showInChannel: other.showInChannel,
command: other.command,
createdAt: other.createdAt,
silent: other.silent,
extraData: other.extraData,
user: other.user,
shadowed: other.shadowed,
updatedAt: other.updatedAt,
deletedAt: other.deletedAt,
status: other.status,
pinned: other.pinned,
pinnedAt: other.pinnedAt,
pinExpires: other.pinExpires,
pinnedBy: other.pinnedBy,
);
}
Message merge(Message other) => copyWith(
id: other.id,
text: other.text,
type: other.type,
attachments: other.attachments,
mentionedUsers: other.mentionedUsers,
reactionCounts: other.reactionCounts,
reactionScores: other.reactionScores,
latestReactions: other.latestReactions,
ownReactions: other.ownReactions,
parentId: other.parentId,
quotedMessage: other.quotedMessage,
quotedMessageId: other.quotedMessageId,
replyCount: other.replyCount,
threadParticipants: other.threadParticipants,
showInChannel: other.showInChannel,
command: other.command,
createdAt: other.createdAt,
silent: other.silent,
extraData: other.extraData,
user: other.user,
shadowed: other.shadowed,
updatedAt: other.updatedAt,
deletedAt: other.deletedAt,
status: other.status,
pinned: other.pinned,
pinnedAt: other.pinnedAt,
pinExpires: other.pinExpires,
pinnedBy: other.pinnedBy,
);
@override
List<Object> get props => [
List<Object?> get props => [
id,
text,
type,
@@ -386,7 +407,7 @@ class Message extends Equatable {
@JsonSerializable()
class TranslatedMessage extends Message {
/// Constructor used for json serialization
TranslatedMessage(this.i18n);
TranslatedMessage(this.i18n) : super();
/// Create a new instance from a json
factory TranslatedMessage.fromJson(Map<String, dynamic> json) =>
@@ -395,7 +416,7 @@ class TranslatedMessage extends Message {
);
/// A Map of
final Map<String, String> i18n;
final Map<String, String>? i18n;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -408,6 +429,5 @@ class TranslatedMessage extends Message {
@override
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$TranslatedMessageToJson(this),
topLevelFields,
);
}
@@ -6,64 +6,44 @@ part of 'message.dart';
// JsonSerializableGenerator
// **************************************************************************
Message _$MessageFromJson(Map json) {
Message _$MessageFromJson(Map<String, dynamic> json) {
return Message(
id: json['id'] as String,
text: json['text'] as String,
type: json['type'] as String,
attachments: (json['attachments'] as List)
?.map((e) => e == null
? null
: Attachment.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
mentionedUsers: (json['mentioned_users'] as List)
?.map((e) => e == null
? null
: User.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
silent: json['silent'] as bool,
shadowed: json['shadowed'] as bool,
reactionCounts: (json['reaction_counts'] as Map)?.map(
(k, e) => MapEntry(k as String, e as int),
id: json['id'] as String?,
text: json['text'] as String?,
type: json['type'] as String? ?? 'regular',
attachments: (json['attachments'] as List<dynamic>?)
?.map((e) => Attachment.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
mentionedUsers: (json['mentioned_users'] as List<dynamic>?)
?.map((e) => User.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
silent: json['silent'] as bool? ?? false,
shadowed: json['shadowed'] as bool? ?? false,
reactionCounts: (json['reaction_counts'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as int),
),
reactionScores: (json['reaction_scores'] as Map)?.map(
(k, e) => MapEntry(k as String, e as int),
reactionScores: (json['reaction_scores'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as int),
),
latestReactions: (json['latest_reactions'] as List)
?.map((e) => e == null
? null
: Reaction.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
ownReactions: (json['own_reactions'] as List)
?.map((e) => e == null
? null
: Reaction.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
parentId: json['parent_id'] as String,
latestReactions: (json['latest_reactions'] as List<dynamic>?)
?.map((e) => Reaction.fromJson(e as Map<String, dynamic>))
.toList(),
ownReactions: (json['own_reactions'] as List<dynamic>?)
?.map((e) => Reaction.fromJson(e as Map<String, dynamic>))
.toList(),
parentId: json['parent_id'] as String?,
quotedMessage: json['quoted_message'] == null
? null
: Message.fromJson((json['quoted_message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
quotedMessageId: json['quoted_message_id'] as String,
replyCount: json['reply_count'] as int,
threadParticipants: (json['thread_participants'] as List)
?.map((e) => e == null
? null
: User.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
showInChannel: json['show_in_channel'] as bool,
command: json['command'] as String,
: Message.fromJson(json['quoted_message'] as Map<String, dynamic>),
quotedMessageId: json['quoted_message_id'] as String?,
replyCount: json['reply_count'] as int?,
threadParticipants: (json['thread_participants'] as List<dynamic>?)
?.map((e) => User.fromJson(e as Map<String, dynamic>))
.toList(),
showInChannel: json['show_in_channel'] as bool?,
command: json['command'] as String?,
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
@@ -72,10 +52,8 @@ Message _$MessageFromJson(Map json) {
: DateTime.parse(json['updated_at'] as String),
user: json['user'] == null
? null
: User.fromJson((json['user'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
pinned: json['pinned'] as bool,
: User.fromJson(json['user'] as Map<String, dynamic>),
pinned: json['pinned'] as bool? ?? false,
pinnedAt: json['pinned_at'] == null
? null
: DateTime.parse(json['pinned_at'] as String),
@@ -84,16 +62,15 @@ Message _$MessageFromJson(Map json) {
: DateTime.parse(json['pin_expires'] as String),
pinnedBy: json['pinned_by'] == null
? null
: User.fromJson((json['pinned_by'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
),
: User.fromJson(json['pinned_by'] as Map<String, dynamic>),
extraData: (json['extra_data'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as Object),
) ??
{},
deletedAt: json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
skipPush: json['skip_push'] as bool,
skipPush: json['skip_push'] as bool? ?? false,
);
}
@@ -110,8 +87,7 @@ Map<String, dynamic> _$MessageToJson(Message instance) {
}
writeNotNull('type', readonly(instance.type));
writeNotNull(
'attachments', instance.attachments?.map((e) => e?.toJson())?.toList());
val['attachments'] = instance.attachments.map((e) => e.toJson()).toList();
val['mentioned_users'] = Serialization.userIds(instance.mentionedUsers);
writeNotNull('reaction_counts', readonly(instance.reactionCounts));
writeNotNull('reaction_scores', readonly(instance.reactionScores));
@@ -134,15 +110,15 @@ Map<String, dynamic> _$MessageToJson(Message instance) {
val['pinned_at'] = readonly(instance.pinnedAt);
val['pin_expires'] = instance.pinExpires?.toIso8601String();
val['pinned_by'] = readonly(instance.pinnedBy);
writeNotNull('extra_data', instance.extraData);
val['extra_data'] = instance.extraData;
writeNotNull('deleted_at', readonly(instance.deletedAt));
return val;
}
TranslatedMessage _$TranslatedMessageFromJson(Map json) {
TranslatedMessage _$TranslatedMessageFromJson(Map<String, dynamic> json) {
return TranslatedMessage(
(json['i18n'] as Map)?.map(
(k, e) => MapEntry(k as String, e as String),
(json['i18n'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
),
);
}
@@ -9,7 +9,12 @@ part 'mute.g.dart';
@JsonSerializable()
class Mute {
/// Constructor used for json serialization
Mute({this.user, this.channel, this.createdAt, this.updatedAt});
Mute({
required this.user,
required this.channel,
required this.createdAt,
required this.updatedAt,
});
/// Create a new instance from a json
factory Mute.fromJson(Map<String, dynamic> json) => _$MuteFromJson(json);
@@ -6,24 +6,12 @@ part of 'mute.dart';
// JsonSerializableGenerator
// **************************************************************************
Mute _$MuteFromJson(Map json) {
Mute _$MuteFromJson(Map<String, dynamic> json) {
return Mute(
user: json['user'] == null
? null
: User.fromJson((json['user'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
channel: json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
updatedAt: json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String),
user: User.fromJson(json['user'] as Map<String, dynamic>),
channel: ChannelModel.fromJson(json['channel'] as Map<String, dynamic>),
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
);
}
@@ -12,19 +12,19 @@ part 'own_user.g.dart';
class OwnUser extends User {
/// Constructor used for json serialization
OwnUser({
this.devices,
this.mutes,
this.totalUnreadCount,
this.devices = const [],
this.mutes = const [],
this.totalUnreadCount = 0,
this.unreadChannels,
this.channelMutes,
String id,
String role,
DateTime createdAt,
DateTime updatedAt,
DateTime lastActive,
bool online,
Map<String, dynamic> extraData,
bool banned,
this.channelMutes = const [],
required String id,
String? role,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? lastActive,
bool online = false,
Map<String, Object> extraData = const {},
bool banned = false,
}) : super(
id: id,
role: role,
@@ -41,24 +41,34 @@ class OwnUser extends User {
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
/// List of user devices
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false,
toJson: Serialization.readOnly,
defaultValue: <Device>[])
final List<Device> devices;
/// List of users muted by the user
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false,
toJson: Serialization.readOnly,
defaultValue: <Mute>[])
final List<Mute> mutes;
/// List of users muted by the user
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false,
toJson: Serialization.readOnly,
defaultValue: <Mute>[])
final List<Mute> channelMutes;
/// Total unread messages by the user
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false, toJson: Serialization.readOnly, defaultValue: 0)
final int totalUnreadCount;
/// Total unread channels by the user
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final int unreadChannels;
final int? unreadChannels;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -74,5 +84,6 @@ class OwnUser extends User {
/// Serialize to json
@override
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$OwnUserToJson(this), topLevelFields);
_$OwnUserToJson(this),
);
}
@@ -6,33 +6,24 @@ part of 'own_user.dart';
// JsonSerializableGenerator
// **************************************************************************
OwnUser _$OwnUserFromJson(Map json) {
OwnUser _$OwnUserFromJson(Map<String, dynamic> json) {
return OwnUser(
devices: (json['devices'] as List)
?.map((e) => e == null
? null
: Device.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
mutes: (json['mutes'] as List)
?.map((e) => e == null
? null
: Mute.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
totalUnreadCount: json['total_unread_count'] as int,
unreadChannels: json['unread_channels'] as int,
channelMutes: (json['channel_mutes'] as List)
?.map((e) => e == null
? null
: Mute.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
devices: (json['devices'] as List<dynamic>?)
?.map((e) => Device.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
mutes: (json['mutes'] as List<dynamic>?)
?.map((e) => Mute.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
totalUnreadCount: json['total_unread_count'] as int? ?? 0,
unreadChannels: json['unread_channels'] as int?,
channelMutes: (json['channel_mutes'] as List<dynamic>?)
?.map((e) => Mute.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
id: json['id'] as String,
role: json['role'] as String,
role: json['role'] as String?,
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
@@ -42,11 +33,11 @@ OwnUser _$OwnUserFromJson(Map json) {
lastActive: json['last_active'] == null
? null
: DateTime.parse(json['last_active'] as String),
online: json['online'] as bool,
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
online: json['online'] as bool? ?? false,
extraData: (json['extra_data'] as Map<String, dynamic>).map(
(k, e) => MapEntry(k, e as Object),
),
banned: json['banned'] as bool,
banned: json['banned'] as bool? ?? false,
);
}
@@ -67,7 +58,7 @@ Map<String, dynamic> _$OwnUserToJson(OwnUser instance) {
writeNotNull('last_active', readonly(instance.lastActive));
writeNotNull('online', readonly(instance.online));
writeNotNull('banned', readonly(instance.banned));
writeNotNull('extra_data', instance.extraData);
val['extra_data'] = instance.extraData;
writeNotNull('devices', readonly(instance.devices));
writeNotNull('mutes', readonly(instance.mutes));
writeNotNull('channel_mutes', readonly(instance.channelMutes));
@@ -10,20 +10,24 @@ class Reaction {
/// Constructor used for json serialization
Reaction({
this.messageId,
this.createdAt,
this.type,
DateTime? createdAt,
required this.type,
this.user,
String userId,
this.score,
String? userId,
this.score = 0,
this.extraData,
}) : userId = userId ?? user?.id;
}) : userId = userId ?? user?.id,
createdAt = createdAt ?? DateTime.now();
/// Create a new instance from a json
factory Reaction.fromJson(Map<String, dynamic> json) => _$ReactionFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
factory Reaction.fromJson(Map<String, dynamic> json) =>
_$ReactionFromJson(Serialization.moveToExtraDataFromRoot(
json,
topLevelFields,
));
/// The messageId to which the reaction belongs
final String messageId;
final String? messageId;
/// The type of the reaction
final String type;
@@ -34,18 +38,19 @@ class Reaction {
/// The user that sent the reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final User user;
final User? user;
/// The score of the reaction (ie. number of reactions sent)
@JsonKey(defaultValue: 0)
final int score;
/// The userId that sent the reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String userId;
final String? userId;
/// Reaction custom extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
final Map<String, Object>? extraData;
/// Map of custom user extraData
static const topLevelFields = [
@@ -59,17 +64,18 @@ class Reaction {
/// Serialize to json
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$ReactionToJson(this), topLevelFields);
_$ReactionToJson(this),
);
/// Creates a copy of [Reaction] with specified attributes overridden.
Reaction copyWith({
String messageId,
DateTime createdAt,
String type,
User user,
String userId,
int score,
Map<String, dynamic> extraData,
String? messageId,
DateTime? createdAt,
String? type,
User? user,
String? userId,
int? score,
Map<String, Object>? extraData,
}) =>
Reaction(
messageId: messageId ?? this.messageId,
@@ -83,16 +89,13 @@ class Reaction {
/// Returns a new [Reaction] that is a combination of this reaction and the
/// given [other] reaction.
Reaction merge(Reaction other) {
if (other == null) return this;
return copyWith(
messageId: other.messageId,
createdAt: other.createdAt,
type: other.type,
user: other.user,
userId: other.userId,
score: other.score,
extraData: other.extraData,
);
}
Reaction merge(Reaction other) => copyWith(
messageId: other.messageId,
createdAt: other.createdAt,
type: other.type,
user: other.user,
userId: other.userId,
score: other.score,
extraData: other.extraData,
);
}
@@ -6,22 +6,20 @@ part of 'reaction.dart';
// JsonSerializableGenerator
// **************************************************************************
Reaction _$ReactionFromJson(Map json) {
Reaction _$ReactionFromJson(Map<String, dynamic> json) {
return Reaction(
messageId: json['message_id'] as String,
messageId: json['message_id'] as String?,
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
type: json['type'] as String,
user: json['user'] == null
? null
: User.fromJson((json['user'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
userId: json['user_id'] as String,
score: json['score'] as int,
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
: User.fromJson(json['user'] as Map<String, dynamic>),
userId: json['user_id'] as String?,
score: json['score'] as int? ?? 0,
extraData: (json['extra_data'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as Object),
),
);
}
@@ -8,9 +8,9 @@ part 'read.g.dart';
class Read {
/// Constructor used for json serialization
Read({
this.lastRead,
this.user,
this.unreadMessages,
required this.lastRead,
required this.user,
this.unreadMessages = 0,
});
/// Create a new instance from a json
@@ -23,6 +23,7 @@ class Read {
final User user;
/// Number of unread messages
@JsonKey(defaultValue: 0)
final int unreadMessages;
/// Serialize to json
@@ -30,9 +31,9 @@ class Read {
/// Creates a copy of [Read] with specified attributes overridden.
Read copyWith({
DateTime lastRead,
User user,
int unreadMessages,
DateTime? lastRead,
User? user,
int? unreadMessages,
}) =>
Read(
lastRead: lastRead ?? this.lastRead,
@@ -6,22 +6,16 @@ part of 'read.dart';
// JsonSerializableGenerator
// **************************************************************************
Read _$ReadFromJson(Map json) {
Read _$ReadFromJson(Map<String, dynamic> json) {
return Read(
lastRead: json['last_read'] == null
? null
: DateTime.parse(json['last_read'] as String),
user: json['user'] == null
? null
: User.fromJson((json['user'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
unreadMessages: json['unread_messages'] as int,
lastRead: DateTime.parse(json['last_read'] as String),
user: User.fromJson(json['user'] as Map<String, dynamic>),
unreadMessages: json['unread_messages'] as int? ?? 0,
);
}
Map<String, dynamic> _$ReadToJson(Read instance) => <String, dynamic>{
'last_read': instance.lastRead?.toIso8601String(),
'user': instance.user?.toJson(),
'last_read': instance.lastRead.toIso8601String(),
'user': instance.user.toJson(),
'unread_messages': instance.unreadMessages,
};
@@ -10,16 +10,14 @@ class Serialization {
static const Function readOnly = readonly;
/// List of users to list of userIds
static List<String> userIds(List<User> users) =>
users?.map((u) => u.id)?.toList();
static List<String>? userIds(List<User>? users) =>
users?.map((u) => u.id).toList();
/// Takes unknown json keys and puts them in the `extra_data` key
static Map<String, dynamic> moveToExtraDataFromRoot(
Map<String, dynamic> json,
List<String> topLevelFields,
) {
if (json == null) return null;
final jsonClone = Map<String, dynamic>.from(json);
final extraDataMap = Map<String, dynamic>.from(json)
@@ -38,7 +36,6 @@ class Serialization {
/// the json map
static Map<String, dynamic> moveFromExtraDataToRoot(
Map<String, dynamic> json,
List<String> topLevelFields,
) {
final jsonClone = Map<String, dynamic>.from(json);
return jsonClone
+39 -41
View File
@@ -8,33 +8,22 @@ part 'user.g.dart';
class User {
/// Constructor used for json serialization
User({
this.id,
required this.id,
this.role,
this.createdAt,
this.updatedAt,
DateTime? createdAt,
DateTime? updatedAt,
this.lastActive,
this.online,
this.extraData,
this.banned,
this.teams,
});
this.online = false,
this.extraData = const {},
this.banned = false,
this.teams = const [],
}) : createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now();
/// Create a new instance from a json
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
/// Use this named constructor to create a new user instance
User.init(
this.id, {
this.online,
this.extraData,
}) : createdAt = null,
updatedAt = null,
lastActive = null,
banned = null,
teams = null,
role = null;
/// Known top level fields.
/// Useful for [Serialization] methods.
static const topLevelFields = [
@@ -53,10 +42,13 @@ class User {
/// User role
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String role;
final String? role;
/// User role
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false,
toJson: Serialization.readOnly,
defaultValue: <String>[])
final List<String> teams;
/// Date of user creation
@@ -69,28 +61,33 @@ class User {
/// Date of last user connection
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime lastActive;
final DateTime? lastActive;
/// True if user is online
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false, toJson: Serialization.readOnly, defaultValue: false)
final bool online;
/// True if user is banned from the chat
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false, toJson: Serialization.readOnly, defaultValue: false)
final bool banned;
/// Map of custom user extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
final Map<String, Object> extraData;
@override
int get hashCode => id.hashCode;
/// Shortcut for user name
String get name =>
(extraData?.containsKey('name') == true && extraData['name'] != '')
? extraData['name']
: id;
String get name {
if (extraData.containsKey('name')) {
final name = extraData['name'] as String;
if (name.isNotEmpty) return name;
}
return id;
}
@override
bool operator ==(Object other) =>
@@ -98,20 +95,21 @@ class User {
other is User && runtimeType == other.runtimeType && id == other.id;
/// Serialize to json
Map<String, dynamic> toJson() =>
Serialization.moveFromExtraDataToRoot(_$UserToJson(this), topLevelFields);
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$UserToJson(this),
);
/// Creates a copy of [User] with specified attributes overridden.
User copyWith({
String id,
String role,
DateTime createdAt,
DateTime updatedAt,
DateTime lastActive,
bool online,
Map<String, dynamic> extraData,
bool banned,
List<String> teams,
String? id,
String? role,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? lastActive,
bool? online,
Map<String, Object>? extraData,
bool? banned,
List<String>? teams,
}) =>
User(
id: id ?? this.id,
@@ -6,10 +6,10 @@ part of 'user.dart';
// JsonSerializableGenerator
// **************************************************************************
User _$UserFromJson(Map json) {
User _$UserFromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as String,
role: json['role'] as String,
role: json['role'] as String?,
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
@@ -19,12 +19,14 @@ User _$UserFromJson(Map json) {
lastActive: json['last_active'] == null
? null
: DateTime.parse(json['last_active'] as String),
online: json['online'] as bool,
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
online: json['online'] as bool? ?? false,
extraData: (json['extra_data'] as Map<String, dynamic>).map(
(k, e) => MapEntry(k, e as Object),
),
banned: json['banned'] as bool,
teams: (json['teams'] as List)?.map((e) => e as String)?.toList(),
banned: json['banned'] as bool? ?? false,
teams:
(json['teams'] as List<dynamic>?)?.map((e) => e as String).toList() ??
[],
);
}
@@ -46,6 +48,6 @@ Map<String, dynamic> _$UserToJson(User instance) {
writeNotNull('last_active', readonly(instance.lastActive));
writeNotNull('online', readonly(instance.online));
writeNotNull('banned', readonly(instance.banned));
writeNotNull('extra_data', instance.extraData);
val['extra_data'] = instance.extraData;
return val;
}
@@ -3,6 +3,7 @@ library stream_chat;
export 'package:async/async.dart';
export 'package:dio/src/dio_error.dart';
export 'package:dio/src/multipart_file.dart';
export 'package:dio/src/options.dart';
export 'package:dio/src/options.dart' show ProgressCallback;
export 'package:logging/logging.dart' show Logger, Level;
@@ -27,6 +28,7 @@ export './src/models/channel_state.dart';
export './src/models/command.dart';
export './src/models/device.dart';
export './src/models/event.dart';
export './src/models/filter.dart' show Filter;
export './src/models/member.dart';
export './src/models/message.dart';
export './src/models/mute.dart';
+1 -1
View File
@@ -3,4 +3,4 @@ import 'package:stream_chat/src/client.dart';
/// Current package version
/// Used in [StreamChatClient] to build the `x-stream-client` header
// ignore: constant_identifier_names
const PACKAGE_VERSION = '1.5.3';
const PACKAGE_VERSION = '2.0.0-nullsafety.0';
+19 -19
View File
@@ -1,31 +1,31 @@
name: stream_chat
homepage: https://getstream.io/
description: The official Dart client for Stream Chat, a service for building chat applications.
version: 1.5.3
version: 2.0.0-nullsafety.0
repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
environment:
sdk: ">=2.7.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'
dependencies:
async: ^2.4.2
collection: ^1.14.13
dio: ^3.0.10
async: ^2.5.0
collection: ^1.15.0
dio: ^4.0.0
equatable: ^2.0.0
freezed_annotation: ^0.12.0
http_parser: ^3.1.4
json_annotation: ^3.0.1
logging: ^0.11.4
meta: ^1.2.4
mime: ^0.9.7
rxdart: ^0.25.0
uuid: ^2.2.2
web_socket_channel: ^1.2.0
freezed_annotation: ^0.14.0
http_parser: ^4.0.0
json_annotation: ^4.0.1
logging: ^1.0.1
meta: ^1.3.0
mime: ^1.0.0
rxdart: ^0.26.0
uuid: ^3.0.4
web_socket_channel: ^2.0.0
dev_dependencies:
build_runner: ^1.10.0
freezed: ^0.12.7
json_serializable: ^3.3.0
mockito: ^4.1.1
test: ^1.15.7
build_runner: ^2.0.1
freezed: ^0.14.1+3
json_serializable: ^4.1.0
mocktail: ^0.1.1
test: ^1.16.8
File diff suppressed because it is too large Load Diff
@@ -1,18 +1,19 @@
import 'package:test/test.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart';
void main() {
group('src/api/requests', () {
test('SortOption', () {
final option = SortOption('name');
const option = SortOption('name');
final j = option.toJson();
expect(j, {'field': 'name', 'direction': -1});
});
test('PaginationParams', () {
final option = PaginationParams();
const option = PaginationParams();
final j = option.toJson();
expect(j, {'limit': 10, 'offset': 0});
expect(j, containsPair('limit', 10));
expect(j, containsPair('offset', 0));
});
});
}
@@ -12,7 +12,8 @@ import 'package:stream_chat/stream_chat.dart';
void main() {
group('src/api/responses', () {
test('QueryChannelsResponse', () {
const jsonExample = r'''{
const jsonExample = r'''
{
"channels": [
{
"channel": {
@@ -3284,7 +3285,7 @@ void main() {
});
test('QueryReactionsResponse', () {
const jsonExample = r'''
const jsonExample = '''
{"reactions": [{"message_id": "4637f7e4-a06b-42db-ba5a-8d8270dd926f","user_id": "c1c9b454-2bcc-402d-8bb0-2f3706ce1680","user": {"id": "c1c9b454-2bcc-402d-8bb0-2f3706ce1680","role": "user","created_at": "2020-01-28T22:17:30.83015Z","updated_at": "2020-01-28T22:17:31.19435Z","banned": false,"online": false,"image": "https://randomuser.me/api/portraits/women/2.jpg","name": "Mia Denys"},"type": "love","score": 1,"created_at": "2020-01-28T22:17:31.128376Z","updated_at": "2020-01-28T22:17:31.128376Z"}]}
''';
final response =
@@ -3402,37 +3403,38 @@ void main() {
test('ListDevicesResponse', () {
const jsonExample =
r'''{"devices":[{"push_provider":"firebase","id":"test"}],"duration":"0.35ms"}''';
'''{"devices":[{"push_provider":"firebase","id":"test"}],"duration":"0.35ms"}''';
final response = ListDevicesResponse.fromJson(json.decode(jsonExample));
expect(response.devices, isA<List<Device>>());
});
test('SendFileResponse', () {
const jsonExample = r'''{"file": "file-url","duration":"0.35ms"}''';
const jsonExample = '''{"file": "file-url","duration":"0.35ms"}''';
final response = SendFileResponse.fromJson(json.decode(jsonExample));
expect(response.file, isA<String>());
});
test('SendImageResponse', () {
const jsonExample = r'''{"file": "file-url","duration":"0.35ms"}''';
const jsonExample = '''{"file": "file-url","duration":"0.35ms"}''';
final response = SendImageResponse.fromJson(json.decode(jsonExample));
expect(response.file, isA<String>());
});
test('SendImageResponse', () {
const jsonExample = r'''{"file": "file-url","duration":"0.35ms"}''';
const jsonExample = '''{"file": "file-url","duration":"0.35ms"}''';
final response = SendImageResponse.fromJson(json.decode(jsonExample));
expect(response.file, isA<String>());
});
test('EmptyResponse', () {
const jsonExample = r'''{"file": "file-url","duration":"0.35ms"}''';
const jsonExample = '''{"file": "file-url","duration":"0.35ms"}''';
final response = EmptyResponse.fromJson(json.decode(jsonExample));
expect(response.duration, isA<String>());
});
test('SendReactionResponse', () {
const jsonExample = r'''{"message": {
const jsonExample = r'''
{"message": {
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3481,8 +3483,8 @@ void main() {
});
test('UpdateUsersResponse', () {
const jsonExample =
r'''{"users": {"bbb19d9a-ee50-45bc-84e5-0584e79d0c9e":{
const jsonExample = '''
{"users": {"bbb19d9a-ee50-45bc-84e5-0584e79d0c9e":{
"id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e",
"role": "user",
"created_at": "2020-01-28T22:17:30.826259Z",
@@ -3498,7 +3500,7 @@ void main() {
test('ConnectGuestUserResponse', () {
const jsonExample =
r'{"user":{"id":"guest-ac612aee-25fe-49fb-b1af-969e41f452a0-wild-breeze-7","role":"guest","created_at":"2020-02-03T10:19:01.538434Z","updated_at":"2020-02-03T10:19:01.539543Z","banned":false,"online":false},"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiZ3Vlc3QtYWM2MTJhZWUtMjVmZS00OWZiLWIxYWYtOTY5ZTQxZjQ1MmEwLXdpbGQtYnJlZXplLTcifQ.mmoFGu7oJjpFsp7nFN78UbIpO7gowbuIbyoppsuvbXA","duration":"4.66ms"}';
'''{"user":{"id":"guest-ac612aee-25fe-49fb-b1af-969e41f452a0-wild-breeze-7","role":"guest","created_at":"2020-02-03T10:19:01.538434Z","updated_at":"2020-02-03T10:19:01.539543Z","banned":false,"online":false},"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiZ3Vlc3QtYWM2MTJhZWUtMjVmZS00OWZiLWIxYWYtOTY5ZTQxZjQ1MmEwLXdpbGQtYnJlZXplLTcifQ.mmoFGu7oJjpFsp7nFN78UbIpO7gowbuIbyoppsuvbXA","duration":"4.66ms"}''';
final response =
ConnectGuestUserResponse.fromJson(json.decode(jsonExample));
expect(response.user, isA<User>());
@@ -3506,7 +3508,8 @@ void main() {
});
test('GetMessagesByIdResponse', () {
const jsonExample = r'''{"messages":[{
const jsonExample = r'''
{"messages":[{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3537,7 +3540,8 @@ void main() {
});
test('SendActionResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3567,7 +3571,8 @@ void main() {
});
test('UpdateMessageResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3597,7 +3602,8 @@ void main() {
});
test('SendMessageResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3627,7 +3633,8 @@ void main() {
});
test('GetMessageResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3657,7 +3664,8 @@ void main() {
});
test('UpdateChannelResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3770,7 +3778,8 @@ void main() {
});
test('InviteMembersResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3883,7 +3892,8 @@ void main() {
});
test('RemoveMembersResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3996,7 +4006,8 @@ void main() {
});
test('AddMembersResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -4109,7 +4120,8 @@ void main() {
});
test('AcceptInviteResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -4222,7 +4234,8 @@ void main() {
});
test('RejectInviteResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -0,0 +1,11 @@
import 'package:test/test.dart';
import 'package:stream_chat/src/api/web_socket_channel_stub.dart';
void main() {
test('src/api/web_socket_stub_test', () {
expect(
() => connectWebSocket('fakeurl'),
throwsA(isA<UnimplementedError>()),
);
});
}
@@ -1,7 +1,7 @@
import 'dart:async';
import 'package:logging/logging.dart';
import 'package:mockito/mockito.dart';
import 'package:mocktail/mocktail.dart';
import 'package:stream_chat/src/api/connection_status.dart';
import 'package:stream_chat/src/api/websocket.dart';
import 'package:stream_chat/src/models/event.dart';
@@ -12,14 +12,12 @@ import 'package:web_socket_channel/web_socket_channel.dart';
class Functions {
WebSocketChannel connectFunc(
String url, {
Iterable<String> protocols,
Map<String, dynamic> headers,
Duration pingInterval,
String? url, {
Iterable<String>? protocols,
}) =>
null;
WebSocketChannel.connect(Uri());
void handleFunc(Event event) => null;
void handleFunc(Event event) {}
}
class MockFunctions extends Mock implements Functions {}
@@ -28,35 +26,35 @@ class MockWSChannel extends Mock implements WebSocketChannel {}
class MockWSSink extends Mock implements WebSocketSink {}
class FakeEvent extends Fake implements Event {}
void main() {
group('src/api/websocket', () {
test('should connect with correct parameters', () async {
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
setUpAll(() {
registerFallbackValue<Event>(FakeEvent());
});
test('should connect with correct parameters', () async {
final connectFunc = MockFunctions().connectFunc;
final ws = WebSocket(
baseUrl: 'baseurl',
user: User(id: 'testid'),
logger: Logger('ws'),
connectParams: {'test': 'true'},
connectPayload: {'payload': 'test'},
handler: (e) {
print(e);
},
handler: print,
connectFunc: connectFunc,
);
final mockWSChannel = MockWSChannel();
final streamController = StreamController<String>.broadcast();
const computedUrl =
'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D';
when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(mockWSChannel.sink).thenAnswer((_) => MockWSSink());
when(mockWSChannel.stream).thenAnswer((_) {
return streamController.stream;
});
when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink());
when(() => mockWSChannel.stream).thenAnswer(
(_) => streamController.stream,
);
final timer = Timer.periodic(
const Duration(milliseconds: 100),
@@ -65,7 +63,7 @@ void main() {
await ws.connect();
verify(connectFunc(computedUrl)).called(1);
verify(() => connectFunc(computedUrl)).called(1);
expect(ws.connectionStatus, ConnectionStatus.connected);
await streamController.close();
@@ -75,8 +73,7 @@ void main() {
test('should connect with correct parameters and handle events', () async {
final handleFunc = MockFunctions().handleFunc;
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
final ws = WebSocket(
baseUrl: 'baseurl',
user: User(id: 'testid'),
@@ -86,27 +83,21 @@ void main() {
handler: handleFunc,
connectFunc: connectFunc,
);
final mockWSChannel = MockWSChannel();
final StreamController<String> streamController =
StreamController<String>.broadcast();
final computedUrl =
final streamController = StreamController<String>.broadcast();
const computedUrl =
'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D';
when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(mockWSChannel.sink).thenAnswer((_) => MockWSSink());
when(mockWSChannel.stream).thenAnswer((_) {
return streamController.stream;
});
when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink());
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
final connect = ws.connect().then((_) {
streamController.sink.add('{}');
return Future.delayed(Duration(milliseconds: 200));
return Future.delayed(const Duration(milliseconds: 200));
}).then((value) {
verify(connectFunc(computedUrl)).called(1);
verify(handleFunc(any)).called(greaterThan(0));
verify(() => connectFunc(computedUrl)).called(1);
verify(() => handleFunc(any())).called(greaterThan(0));
return streamController.close();
});
@@ -118,9 +109,7 @@ void main() {
test('should close correctly the controller', () async {
final handleFunc = MockFunctions().handleFunc;
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
final ws = WebSocket(
baseUrl: 'baseurl',
user: User(id: 'testid'),
@@ -130,27 +119,23 @@ void main() {
handler: handleFunc,
connectFunc: connectFunc,
);
final mockWSChannel = MockWSChannel();
final StreamController<String> streamController =
StreamController<String>.broadcast();
final computedUrl =
final streamController = StreamController<String>.broadcast();
const computedUrl =
'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D';
when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(mockWSChannel.sink).thenAnswer((_) => MockWSSink());
when(mockWSChannel.stream).thenAnswer((_) {
return streamController.stream;
});
final mockWSSink = MockWSSink();
when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink);
when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null);
when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
final connect = ws.connect().then((_) {
streamController.sink.add('{}');
return Future.delayed(Duration(milliseconds: 200));
return Future.delayed(const Duration(milliseconds: 200));
}).then((value) {
verify(connectFunc(computedUrl)).called(1);
verify(handleFunc(any)).called(greaterThan(0));
verify(() => connectFunc(computedUrl)).called(1);
verify(() => handleFunc(any())).called(greaterThan(0));
return streamController.close();
});
@@ -159,10 +144,11 @@ void main() {
return connect;
});
test('should close correctly the controller while connecting', () async {
final handleFunc = MockFunctions().handleFunc;
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
final ws = WebSocket(
baseUrl: 'baseurl',
@@ -176,31 +162,30 @@ void main() {
final mockWSChannel = MockWSChannel();
final StreamController<String> streamController =
StreamController<String>.broadcast();
final streamController = StreamController<String>.broadcast();
final computedUrl =
const computedUrl =
'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D';
when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(mockWSChannel.sink).thenAnswer((_) => MockWSSink());
when(mockWSChannel.stream).thenAnswer((_) {
return streamController.stream;
});
final mockWSSink = MockWSSink();
when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink);
when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null);
when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
ws.connect();
await ws.disconnect();
streamController.add('{}');
verify(connectFunc(computedUrl)).called(1);
verifyNever(handleFunc(any));
verify(() => connectFunc(computedUrl)).called(1);
verifyNever(() => handleFunc(any()));
addTearDown(streamController.close);
});
test('should run correctly health check', () async {
final handleFunc = MockFunctions().handleFunc;
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
final ws = WebSocket(
baseUrl: 'baseurl',
user: User(id: 'testid'),
@@ -210,32 +195,28 @@ void main() {
handler: handleFunc,
connectFunc: connectFunc,
);
final mockWSChannel = MockWSChannel();
final mockWSSink = MockWSSink();
final StreamController<String> streamController =
StreamController<String>.broadcast();
final computedUrl =
final streamController = StreamController<String>.broadcast();
const computedUrl =
'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D';
when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(mockWSChannel.stream).thenAnswer((_) {
return streamController.stream;
});
when(mockWSChannel.sink).thenReturn(mockWSSink);
when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink);
when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null);
final timer = Timer.periodic(
Duration(milliseconds: 1000),
const Duration(milliseconds: 1000),
(_) => streamController.sink.add('{}'),
);
final connect = ws.connect().then((_) {
streamController.sink.add('{}');
return Future.delayed(Duration(milliseconds: 200));
return Future.delayed(const Duration(milliseconds: 200));
}).then((value) async {
verify(mockWSSink.add("{'type': 'health.check'}")).called(greaterThan(0));
verify(() => mockWSSink.add("{'type': 'health.check'}"))
.called(greaterThan(0));
timer.cancel();
await streamController.close();
@@ -249,9 +230,7 @@ void main() {
test('should run correctly reconnection check', () async {
final handleFunc = MockFunctions().handleFunc;
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
Logger.root.level = Level.ALL;
final ws = WebSocket(
baseUrl: 'baseurl',
@@ -262,34 +241,29 @@ void main() {
handler: handleFunc,
connectFunc: connectFunc,
reconnectionMonitorTimeout: 1,
reconnectionMonitorInterval: 1,
);
final mockWSChannel = MockWSChannel();
final mockWSSink = MockWSSink();
StreamController<String> streamController =
StreamController<String>.broadcast();
final computedUrl =
var streamController = StreamController<String>.broadcast();
const computedUrl =
'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D';
when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(mockWSChannel.stream).thenAnswer((_) {
return streamController.stream;
});
when(mockWSChannel.sink).thenReturn(mockWSSink);
when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink);
when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null);
final connect = ws.connect().then((_) {
streamController.sink.add('{}');
streamController.close();
streamController = StreamController<String>.broadcast();
streamController.sink.add('{}');
return Future.delayed(Duration(milliseconds: 200));
return Future.delayed(const Duration(milliseconds: 200));
}).then((value) async {
verify(mockWSSink.add("{'type': 'health.check'}")).called(greaterThan(0));
verify(() => mockWSSink.add("{'type': 'health.check'}"))
.called(greaterThan(0));
verify(connectFunc(computedUrl)).called(2);
verify(() => connectFunc(computedUrl)).called(2);
await streamController.close();
return mockWSSink.close();
@@ -302,9 +276,7 @@ void main() {
test('should close correctly the controller', () async {
final handleFunc = MockFunctions().handleFunc;
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
final ws = WebSocket(
baseUrl: 'baseurl',
user: User(id: 'testid'),
@@ -314,28 +286,23 @@ void main() {
handler: handleFunc,
connectFunc: connectFunc,
);
final mockWSChannel = MockWSChannel();
final mockWSSink = MockWSSink();
final StreamController<String> streamController =
StreamController<String>.broadcast();
final computedUrl =
final streamController = StreamController<String>.broadcast();
const computedUrl =
'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D';
when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(mockWSChannel.stream).thenAnswer((_) {
return streamController.stream;
});
when(mockWSChannel.sink).thenReturn(mockWSSink);
when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink);
when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null);
final connect = ws.connect().then((_) {
streamController.sink.add('{}');
return Future.delayed(Duration(milliseconds: 200));
return Future.delayed(const Duration(milliseconds: 200));
}).then((value) async {
await ws.disconnect();
verify(mockWSSink.close()).called(greaterThan(0));
verify(mockWSSink.close).called(greaterThan(0));
await streamController.close();
await mockWSSink.close();
@@ -347,42 +314,35 @@ void main() {
});
test('should throw an error', () async {
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
final ws = WebSocket(
baseUrl: 'baseurl',
user: User(id: 'testid'),
logger: Logger('ws'),
connectParams: {'test': 'true'},
connectPayload: {'payload': 'test'},
handler: (e) {
print(e);
},
handler: print,
connectFunc: connectFunc,
);
final mockWSChannel = MockWSChannel();
final streamController = StreamController<String>.broadcast();
final computedUrl =
const computedUrl =
'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D';
when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(mockWSChannel.sink).thenAnswer((_) => MockWSSink());
when(mockWSChannel.stream).thenAnswer((_) {
return streamController.stream;
});
when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink());
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
Future.delayed(
Duration(milliseconds: 1000),
const Duration(milliseconds: 1000),
() => streamController.sink.addError('test error'),
);
try {
expect(await ws.connect(), throwsA(isA<String>()));
} catch (e) {
verify(connectFunc(computedUrl)).called(greaterThanOrEqualTo(1));
verify(() => connectFunc(computedUrl)).called(greaterThanOrEqualTo(1));
streamController.close();
}
});
}
File diff suppressed because it is too large Load Diff
@@ -5,7 +5,8 @@ import 'package:stream_chat/src/models/action.dart';
void main() {
group('src/models/action', () {
const jsonExample = r'''{
const jsonExample = '''
{
"name": "name",
"style": "style",
"text": "text",
@@ -1,12 +1,13 @@
import 'package:stream_chat/src/models/attachment.dart';
import 'package:stream_chat/src/models/action.dart';
import 'dart:convert';
import 'package:stream_chat/src/models/action.dart';
import 'package:stream_chat/src/models/attachment.dart';
import 'package:test/test.dart';
void main() {
group('src/models/attachment', () {
const jsonExample = r'''{
const jsonExample = '''
{
"type": "giphy",
"title": "awesome",
"title_link": "https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti",
@@ -38,22 +39,27 @@ void main() {
test('should parse json correctly', () {
final attachment = Attachment.fromJson(json.decode(jsonExample));
expect(attachment.type, "giphy");
expect(attachment.title, "awesome");
expect(attachment.titleLink,
"https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti");
expect(attachment.thumbUrl,
"https://media0.giphy.com/media/3o7TKnCdBx5cMg0qti/giphy.gif");
expect(attachment.type, 'giphy');
expect(attachment.title, 'awesome');
expect(
attachment.titleLink,
'https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti',
);
expect(
attachment.thumbUrl,
'https://media0.giphy.com/media/3o7TKnCdBx5cMg0qti/giphy.gif',
);
expect(attachment.actions, hasLength(3));
expect(attachment.actions[0], isA<Action>());
});
test('should serialize to json correctly', () {
final channel = Attachment(
type: "image",
title: "soo",
titleLink:
"https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti");
type: 'image',
title: 'soo',
titleLink:
'https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti',
);
expect(
channel.toJson(),
@@ -61,7 +67,8 @@ void main() {
'type': 'image',
'title': 'soo',
'title_link':
'https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti'
'https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti',
'actions': [],
},
);
});
@@ -1,16 +1,17 @@
import 'dart:convert';
import 'package:test/test.dart';
import 'package:stream_chat/src/models/channel_config.dart';
import 'package:stream_chat/src/models/channel_state.dart';
import 'package:stream_chat/src/models/command.dart';
import 'package:stream_chat/src/models/message.dart';
import 'package:stream_chat/src/models/user.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart';
void main() {
group('src/models/channel_state', () {
const jsonExample = r'''{
const jsonExample = '''
{
"channel": {
"id": "dev",
"type": "team",
@@ -844,37 +845,41 @@ void main() {
test('should parse json correctly', () {
final channelState = ChannelState.fromJson(json.decode(jsonExample));
expect(channelState.channel.cid, 'team:dev');
expect(channelState.channel.id, 'dev');
expect(channelState.channel.team, 'test');
expect(channelState.channel.type, 'team');
expect(channelState.channel.config, isA<ChannelConfig>());
expect(channelState.channel.config, isNotNull);
expect(channelState.channel.config.commands, hasLength(1));
expect(channelState.channel.config.commands[0], isA<Command>());
expect(channelState.channel.lastMessageAt,
DateTime.parse("2020-01-30T13:43:41.062362Z"));
expect(channelState.channel.createdAt,
DateTime.parse("2019-04-03T18:43:33.213373Z"));
expect(channelState.channel.updatedAt,
DateTime.parse("2019-04-03T18:43:33.213374Z"));
expect(channelState.channel.createdBy, isA<User>());
expect(channelState.channel.frozen, true);
expect(channelState.channel.extraData['example'], 1);
expect(channelState.channel.extraData['name'], "#dev");
expect(channelState.channel.extraData['image'],
"https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png");
expect(channelState.channel?.cid, 'team:dev');
expect(channelState.channel?.id, 'dev');
expect(channelState.channel?.team, 'test');
expect(channelState.channel?.type, 'team');
expect(channelState.channel?.config, isA<ChannelConfig>());
expect(channelState.channel?.config, isNotNull);
expect(channelState.channel?.config.commands, hasLength(1));
expect(channelState.channel?.config.commands[0], isA<Command>());
expect(channelState.channel?.lastMessageAt,
DateTime.parse('2020-01-30T13:43:41.062362Z'));
expect(channelState.channel?.createdAt,
DateTime.parse('2019-04-03T18:43:33.213373Z'));
expect(channelState.channel?.updatedAt,
DateTime.parse('2019-04-03T18:43:33.213374Z'));
expect(channelState.channel?.createdBy, isA<User>());
expect(channelState.channel?.frozen, true);
expect(channelState.channel?.extraData['example'], 1);
expect(channelState.channel?.extraData['name'], '#dev');
expect(
channelState.channel?.extraData['image'],
'https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png',
);
expect(channelState.messages, hasLength(25));
expect(channelState.messages[0], isA<Message>());
expect(channelState.messages[0], isNotNull);
expect(channelState.messages[0].createdAt,
DateTime.parse("2020-01-29T03:23:02.843948Z"));
expect(
channelState.messages[0].createdAt,
DateTime.parse('2020-01-29T03:23:02.843948Z'),
);
expect(channelState.messages[0].user, isA<User>());
expect(channelState.watcherCount, 5);
});
test('should serialize to json correctly', () {
const toJsonExample = r'''
const toJsonExample = '''
{
"channel": {
"id": "dev",
@@ -884,8 +889,8 @@ void main() {
"image": "https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png",
"example": 1
},
"watchers": null,
"read": null,
"watchers": [],
"read": [],
"messages": [
{
"id": "dry-meadow-0-2b73cc8b-cd86-4a01-8d40-bd82ad07a030",
@@ -897,7 +902,7 @@ void main() {
"show_in_channel": null,
"mentioned_users": [],
"status": "SENT",
"skip_push": null,
"skip_push": false,
"silent": false,
"pinned": false,
"pinned_at": null,
@@ -914,7 +919,7 @@ void main() {
"show_in_channel": null,
"mentioned_users": [],
"status": "SENT",
"skip_push": null,
"skip_push": false,
"silent": false,
"pinned": false,
"pinned_at": null,
@@ -924,7 +929,7 @@ void main() {
{
"id": "dry-meadow-0-53e6299f-9b97-4a9c-a27e-7e2dde49b7e0",
"text": "test message",
"skip_push": null,
"skip_push": false,
"attachments": [],
"parent_id": null,
"quoted_message": null,
@@ -947,7 +952,7 @@ void main() {
"quoted_message_id": null,
"show_in_channel": null,
"mentioned_users": [],
"skip_push": null,
"skip_push": false,
"status": "SENT",
"silent": false,
"pinned": false,
@@ -959,7 +964,7 @@ void main() {
"id": "dry-meadow-0-64d7970f-ede8-4b31-9738-1bc1756d2bfe",
"text": "test",
"attachments": [],
"skip_push": null,
"skip_push": false,
"parent_id": null,
"quoted_message": null,
"quoted_message_id": null,
@@ -977,7 +982,7 @@ void main() {
"text": "hi",
"attachments": [],
"parent_id": null,
"skip_push": null,
"skip_push": false,
"quoted_message": null,
"quoted_message_id": null,
"show_in_channel": null,
@@ -995,7 +1000,7 @@ void main() {
"attachments": [],
"parent_id": null,
"quoted_message": null,
"skip_push": null,
"skip_push": false,
"quoted_message_id": null,
"show_in_channel": null,
"mentioned_users": [],
@@ -1018,7 +1023,7 @@ void main() {
"status": "SENT",
"silent": false,
"pinned": false,
"skip_push": null,
"skip_push": false,
"pinned_at": null,
"pin_expires": null,
"pinned_by": null
@@ -1036,7 +1041,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
},
@@ -1048,7 +1053,7 @@ void main() {
"quoted_message": null,
"quoted_message_id": null,
"show_in_channel": null,
"skip_push": null,
"skip_push": false,
"mentioned_users": [],
"status": "SENT",
"silent": false,
@@ -1066,7 +1071,7 @@ void main() {
"quoted_message_id": null,
"show_in_channel": null,
"mentioned_users": [],
"skip_push": null,
"skip_push": false,
"status": "SENT",
"silent": false,
"pinned": false,
@@ -1085,7 +1090,7 @@ void main() {
"mentioned_users": [],
"status": "SENT",
"silent": false,
"skip_push": null,
"skip_push": false,
"pinned": false,
"pinned_at": null,
"pin_expires": null,
@@ -1095,7 +1100,7 @@ void main() {
"id": "icy-recipe-7-935c396e-ddf8-4a9a-951c-0a12fa5bf055",
"text": "what are you doing?",
"attachments": [],
"skip_push": null,
"skip_push": false,
"parent_id": null,
"quoted_message": null,
"quoted_message_id": null,
@@ -1113,7 +1118,7 @@ void main() {
"text": "👍",
"attachments": [],
"parent_id": null,
"skip_push": null,
"skip_push": false,
"quoted_message": null,
"quoted_message_id": null,
"show_in_channel": null,
@@ -1129,7 +1134,7 @@ void main() {
"id": "snowy-credit-3-3e0c1a0d-d22f-42ee-b2a1-f9f49477bf21",
"text": "sdasas",
"attachments": [],
"skip_push": null,
"skip_push": false,
"parent_id": null,
"quoted_message": null,
"quoted_message_id": null,
@@ -1150,7 +1155,7 @@ void main() {
"quoted_message": null,
"quoted_message_id": null,
"show_in_channel": null,
"skip_push": null,
"skip_push": false,
"mentioned_users": [],
"status": "SENT",
"silent": false,
@@ -1163,7 +1168,7 @@ void main() {
"id": "snowy-credit-3-cfaf0b46-1daa-49c5-947c-b16d6697487d",
"text": "nhisagdhsadz",
"attachments": [],
"skip_push": null,
"skip_push": false,
"parent_id": null,
"quoted_message": null,
"quoted_message_id": null,
@@ -1182,7 +1187,7 @@ void main() {
"attachments": [],
"parent_id": null,
"quoted_message": null,
"skip_push": null,
"skip_push": false,
"quoted_message_id": null,
"show_in_channel": null,
"mentioned_users": [],
@@ -1199,7 +1204,7 @@ void main() {
"attachments": [],
"parent_id": null,
"quoted_message": null,
"skip_push": null,
"skip_push": false,
"quoted_message_id": null,
"show_in_channel": null,
"mentioned_users": [],
@@ -1207,7 +1212,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
},
@@ -1224,7 +1229,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
},
@@ -1241,7 +1246,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
},
@@ -1258,7 +1263,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
},
@@ -1275,7 +1280,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
},
@@ -1292,7 +1297,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
},
@@ -1309,7 +1314,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
}
@@ -1325,10 +1330,10 @@ void main() {
members: [],
messages:
(j['messages'] as List).map((m) => Message.fromJson(m)).toList(),
read: null,
read: [],
watcherCount: 5,
pinnedMessages: [],
watchers: null,
watchers: [],
);
expect(
@@ -1,7 +1,7 @@
import 'dart:convert';
import 'package:test/test.dart';
import 'package:stream_chat/src/models/channel_model.dart';
import 'package:test/test.dart';
void main() {
group('src/models/channel', () {
@@ -9,7 +9,7 @@ void main() {
{
"id": "test",
"type": "livestream",
"cid": "test:livestream",
"cid": "livestream:test",
"cats": true,
"fruit": ["bananas", "apples"]
}
@@ -17,34 +17,33 @@ void main() {
test('should parse json correctly', () {
final channel = ChannelModel.fromJson(json.decode(jsonExample));
expect(channel.id, equals("test"));
expect(channel.type, equals("livestream"));
expect(channel.cid, equals("test:livestream"));
expect(channel.extraData["cats"], equals(true));
expect(channel.extraData["fruit"], equals(["bananas", "apples"]));
expect(channel.id, equals('test'));
expect(channel.type, equals('livestream'));
expect(channel.cid, equals('livestream:test'));
expect(channel.extraData['cats'], equals(true));
expect(channel.extraData['fruit'], equals(['bananas', 'apples']));
});
test('should serialize to json correctly', () {
final channel = ChannelModel(
type: "type",
id: "id",
cid: "a:a",
extraData: {"name": "cool"},
type: 'type',
id: 'id',
cid: 'a:a',
extraData: {'name': 'cool'},
);
expect(
channel.toJson(),
{'id': 'id', 'type': 'type', 'name': 'cool'},
{'id': 'id', 'type': 'type', 'frozen': false, 'name': 'cool'},
);
});
test('should serialize to json correctly when frozen is provided', () {
final channel = ChannelModel(
type: "type",
id: "id",
cid: "a:a",
extraData: {"name": "cool"},
frozen: false,
type: 'type',
id: 'id',
cid: 'a:a',
extraData: {'name': 'cool'},
);
expect(
@@ -1,6 +1,6 @@
import 'package:stream_chat/src/models/command.dart';
import 'dart:convert';
import 'package:stream_chat/src/models/command.dart';
import 'package:test/test.dart';
void main() {
@@ -30,9 +30,9 @@ void main() {
expect(
command.toJson(),
{
"name": "giphy",
"description": "Post a random gif to the channel",
"args": "[text]",
'name': 'giphy',
'description': 'Post a random gif to the channel',
'args': '[text]',
},
);
});
@@ -5,7 +5,8 @@ import 'package:stream_chat/src/models/device.dart';
void main() {
group('src/models/device', () {
const jsonExample = r'''{
const jsonExample = '''
{
"id": "device-id",
"push_provider": "push-provider"
}''';
@@ -1,9 +1,9 @@
import 'dart:convert';
import 'package:test/test.dart';
import 'package:stream_chat/src/models/event.dart';
import 'package:stream_chat/src/models/own_user.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart';
void main() {
group('src/models/event', () {
@@ -47,6 +47,7 @@ void main() {
expect(event.createdAt, isA<DateTime>());
expect(event.me, isA<OwnUser>());
expect(event.user, isA<User>());
expect(event.isLocal, false);
});
test('should serialize to json correctly', () {
@@ -55,7 +56,7 @@ void main() {
type: 'type',
cid: 'cid',
connectionId: 'connectionId',
createdAt: DateTime.parse("2020-01-29T03:22:47.63613Z"),
createdAt: DateTime.parse('2020-01-29T03:22:47.63613Z'),
me: OwnUser(id: 'id2'),
totalUnreadCount: 1,
unreadChannels: 1,
@@ -77,11 +78,11 @@ void main() {
'total_unread_count': 1,
'unread_channels': 1,
'online': true,
'is_local': true,
'member': null,
'channel_id': null,
'channel_type': null,
'parent_id': null,
'is_local': true,
},
);
});
@@ -0,0 +1,212 @@
import 'dart:convert';
import 'package:test/test.dart';
import 'package:stream_chat/src/models/filter.dart';
void main() {
group('operators', () {
test('equal', () {
const key = 'testKey';
const value = 'testValue';
final filter = Filter.equal(key, value);
expect(filter.key, key);
expect(filter.value, value);
expect(filter.operator, FilterOperator.equal.rawValue);
});
test('notEqual', () {
const key = 'testKey';
const value = 'testValue';
final filter = Filter.notEqual(key, value);
expect(filter.key, key);
expect(filter.value, value);
expect(filter.operator, FilterOperator.notEqual.rawValue);
});
test('greater', () {
const key = 'testKey';
const value = 'testValue';
final filter = Filter.greater(key, value);
expect(filter.key, key);
expect(filter.value, value);
expect(filter.operator, FilterOperator.greater.rawValue);
});
test('greaterOrEqual', () {
const key = 'testKey';
const value = 'testValue';
final filter = Filter.greaterOrEqual(key, value);
expect(filter.key, key);
expect(filter.value, value);
expect(filter.operator, FilterOperator.greaterOrEqual.rawValue);
});
test('less', () {
const key = 'testKey';
const value = 'testValue';
final filter = Filter.less(key, value);
expect(filter.key, key);
expect(filter.value, value);
expect(filter.operator, FilterOperator.less.rawValue);
});
test('lessOrEqual', () {
const key = 'testKey';
const value = 'testValue';
final filter = Filter.lessOrEqual(key, value);
expect(filter.key, key);
expect(filter.value, value);
expect(filter.operator, FilterOperator.lessOrEqual.rawValue);
});
test('in', () {
const key = 'testKey';
const values = ['testValue'];
final filter = Filter.in_(key, values);
expect(filter.key, key);
expect(filter.value, values);
expect(filter.operator, FilterOperator.in_.rawValue);
});
test('in', () {
const key = 'testKey';
const values = ['testValue'];
final filter = Filter.in_(key, values);
expect(filter.key, key);
expect(filter.value, values);
expect(filter.operator, FilterOperator.in_.rawValue);
});
test('notIn', () {
const key = 'testKey';
const values = ['testValue'];
final filter = Filter.notIn(key, values);
expect(filter.key, key);
expect(filter.value, values);
expect(filter.operator, FilterOperator.notIn.rawValue);
});
test('query', () {
const key = 'testKey';
const value = 'testQuery';
final filter = Filter.query(key, value);
expect(filter.key, key);
expect(filter.value, value);
expect(filter.operator, FilterOperator.query.rawValue);
});
test('autoComplete', () {
const key = 'testKey';
const value = 'testQuery';
final filter = Filter.autoComplete(key, value);
expect(filter.key, key);
expect(filter.value, value);
expect(filter.operator, FilterOperator.autoComplete.rawValue);
});
test('exists', () {
const key = 'testKey';
final filter = Filter.exists(key);
expect(filter.key, key);
expect(filter.value, isTrue);
expect(filter.operator, FilterOperator.exists.rawValue);
});
test('notExists', () {
const key = 'testKey';
final filter = Filter.exists(key, exists: false);
expect(filter.key, key);
expect(filter.value, isFalse);
expect(filter.operator, FilterOperator.exists.rawValue);
});
test('custom', () {
const key = 'testKey';
const value = 'testValue';
const operator = '\$customOperator';
const filter = Filter.custom(operator: operator, key: key, value: value);
expect(filter.key, key);
expect(filter.value, value);
expect(filter.operator, operator);
});
group('groupedOperator', () {
final filter1 = Filter.equal('testKey', 'testValue');
final filter2 = Filter.in_('testKey', const ['testValue']);
final filters = [filter1, filter2];
test('and', () {
final filter = Filter.and(filters);
expect(filter.key, isNull);
expect(filter.value, filters);
expect(filter.operator, FilterOperator.and.rawValue);
});
test('or', () {
final filter = Filter.or(filters);
expect(filter.key, isNull);
expect(filter.value, filters);
expect(filter.operator, FilterOperator.or.rawValue);
});
test('nor', () {
final filter = Filter.nor(filters);
expect(filter.key, isNull);
expect(filter.value, filters);
expect(filter.operator, FilterOperator.nor.rawValue);
});
});
});
group('encoding', () {
group('nonGroupedFilter', () {
test('simpleValue', () {
const key = 'testKey';
const value = 'testValue';
final filter = Filter.equal(key, value);
final encoded = json.encode(filter);
expect(
encoded,
'{"$key":{"${FilterOperator.equal.rawValue}":${json.encode(value)}}}',
);
});
test('listValue', () {
const key = 'testKey';
const values = ['testValue'];
final filter = Filter.in_(key, values);
final encoded = json.encode(filter);
expect(
encoded,
'{"$key":{"${FilterOperator.in_.rawValue}":${json.encode(values)}}}',
);
});
});
test('groupedFilter', () {
final filter1 = Filter.equal('testKey', 'testValue');
final filter2 = Filter.in_('testKey', const ['testValue']);
final filters = [filter1, filter2];
final filter = Filter.and(filters);
final encoded = json.encode(filter);
expect(
encoded,
'{"${FilterOperator.and.rawValue}":${json.encode(filters)}}',
);
});
group('equality', () {
test('simpleFilter', () {
final filter1 = Filter.equal('testKey', 'testValue');
final filter2 = Filter.equal('testKey', 'testValue');
expect(filter1, filter2);
});
test('groupedFilter', () {
final filter1 = Filter.and([Filter.equal('testKey', 'testValue')]);
final filter2 = Filter.and([Filter.equal('testKey', 'testValue')]);
expect(filter1, filter2);
});
});
});
}
@@ -28,8 +28,8 @@ void main() {
final member = Member.fromJson(json.decode(jsonExample));
expect(member.user, isA<User>());
expect(member.role, 'member');
expect(member.createdAt, DateTime.parse("2020-01-28T22:17:30.95443Z"));
expect(member.updatedAt, DateTime.parse("2020-01-28T22:17:30.95443Z"));
expect(member.createdAt, DateTime.parse('2020-01-28T22:17:30.95443Z'));
expect(member.updatedAt, DateTime.parse('2020-01-28T22:17:30.95443Z'));
});
});
}
@@ -1,14 +1,15 @@
import 'dart:convert';
import 'package:test/test.dart';
import 'package:stream_chat/src/models/attachment.dart';
import 'package:stream_chat/src/models/message.dart';
import 'package:stream_chat/src/models/reaction.dart';
import 'package:stream_chat/src/models/user.dart';
import 'package:test/test.dart';
void main() {
group('src/models/message', () {
const jsonExample = r'''{
const jsonExample = r'''
{
"id": "4637f7e4-a06b-42db-ba5a-8d8270dd926f",
"text": "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA",
"type": "regular",
@@ -76,10 +77,10 @@ void main() {
test('should parse json correctly', () {
final message = Message.fromJson(json.decode(jsonExample));
expect(message.id, "4637f7e4-a06b-42db-ba5a-8d8270dd926f");
expect(message.id, '4637f7e4-a06b-42db-ba5a-8d8270dd926f');
expect(message.text,
"https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA");
expect(message.type, "regular");
'https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA');
expect(message.type, 'regular');
expect(message.user, isA<User>());
expect(message.silent, isA<bool>());
expect(message.attachments, isA<List<Attachment>>());
@@ -87,8 +88,8 @@ void main() {
expect(message.ownReactions, isA<List<Reaction>>());
expect(message.reactionCounts, {'love': 1});
expect(message.reactionScores, {'love': 1});
expect(message.createdAt, DateTime.parse("2020-01-28T22:17:31.107978Z"));
expect(message.updatedAt, DateTime.parse("2020-01-28T22:17:31.130506Z"));
expect(message.createdAt, DateTime.parse('2020-01-28T22:17:31.107978Z'));
expect(message.updatedAt, DateTime.parse('2020-01-28T22:17:31.130506Z'));
expect(message.mentionedUsers, isA<List<User>>());
expect(message.pinned, false);
expect(message.pinnedAt, null);
@@ -98,43 +99,41 @@ void main() {
test('should serialize to json correctly', () {
final message = Message(
id: "4637f7e4-a06b-42db-ba5a-8d8270dd926f",
id: '4637f7e4-a06b-42db-ba5a-8d8270dd926f',
text:
"https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA",
silent: false,
'https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA',
attachments: [
Attachment.fromJson({
"type": "video",
"author_name": "GIPHY",
"title": "The Lion King Disney GIF - Find \u0026 Share on GIPHY",
"title_link":
"https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif",
"text":
"Discover \u0026 share this Lion King Live Action GIF with everyone you know. GIPHY is how you search, share, discover, and create GIFs.",
"image_url":
"https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif",
"thumb_url":
"https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif",
"asset_url":
"https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.mp4",
"og_scrape_url":
"https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA"
Attachment.fromJson(const {
'type': 'video',
'author_name': 'GIPHY',
'title': 'The Lion King Disney GIF - Find \u0026 Share on GIPHY',
'title_link':
'https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif',
'text':
'''Discover \u0026 share this Lion King Live Action GIF with everyone you know. GIPHY is how you search, share, discover, and create GIFs.''',
'image_url':
'https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif',
'thumb_url':
'https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif',
'asset_url':
'https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.mp4',
'og_scrape_url':
'https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA'
})
],
showInChannel: true,
parentId: 'parentId',
extraData: {'hey': 'test'},
status: MessageSendingStatus.sent,
extraData: const {'hey': 'test'},
);
expect(
message.toJson(),
json.decode(r'''
json.decode('''
{
"id": "4637f7e4-a06b-42db-ba5a-8d8270dd926f",
"text": "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA",
"silent": false,
"skip_push": null,
"skip_push": false,
"attachments": [
{
"type": "video",
@@ -145,10 +144,11 @@ void main() {
"og_scrape_url": "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA",
"image_url": "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif",
"author_name": "GIPHY",
"asset_url": "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.mp4"
"asset_url": "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.mp4",
"actions": []
}
],
"mentioned_users": null,
"mentioned_users": [],
"parent_id": "parentId",
"quoted_message": null,
"quoted_message_id": null,
@@ -1,8 +1,8 @@
import 'package:test/test.dart';
import 'package:stream_chat/src/models/reaction.dart';
import 'dart:convert';
import 'package:stream_chat/src/models/reaction.dart';
import 'package:stream_chat/src/models/user.dart';
import 'package:test/test.dart';
void main() {
group('src/models/reaction', () {
@@ -30,13 +30,13 @@ void main() {
test('should parse json correctly', () {
final reaction = Reaction.fromJson(json.decode(jsonExample));
expect(reaction.messageId, '76cd8c82-b557-4e48-9d12-87995d3a0e04');
expect(reaction.createdAt, DateTime.parse("2020-01-28T22:17:31.108742Z"));
expect(reaction.createdAt, DateTime.parse('2020-01-28T22:17:31.108742Z'));
expect(reaction.type, 'wow');
expect(
reaction.user.toJson(),
User.init("2de0297c-f3f2-489d-b930-ef77342edccf", extraData: {
"image": "https://randomuser.me/api/portraits/women/45.jpg",
"name": "Daisy Morgan"
reaction.user?.toJson(),
User(id: '2de0297c-f3f2-489d-b930-ef77342edccf', extraData: {
'image': 'https://randomuser.me/api/portraits/women/45.jpg',
'name': 'Daisy Morgan'
}).toJson(),
);
expect(reaction.score, 1);
@@ -47,13 +47,13 @@ void main() {
test('should serialize to json correctly', () {
final reaction = Reaction(
messageId: '76cd8c82-b557-4e48-9d12-87995d3a0e04',
createdAt: DateTime.parse("2020-01-28T22:17:31.108742Z"),
createdAt: DateTime.parse('2020-01-28T22:17:31.108742Z'),
type: 'wow',
user: User.init("2de0297c-f3f2-489d-b930-ef77342edccf", extraData: {
"image": "https://randomuser.me/api/portraits/women/45.jpg",
"name": "Daisy Morgan"
user: User(id: '2de0297c-f3f2-489d-b930-ef77342edccf', extraData: {
'image': 'https://randomuser.me/api/portraits/women/45.jpg',
'name': 'Daisy Morgan'
}),
userId: "2de0297c-f3f2-489d-b930-ef77342edccf",
userId: '2de0297c-f3f2-489d-b930-ef77342edccf',
extraData: {'bananas': 'yes'},
score: 1,
);
@@ -61,10 +61,10 @@ void main() {
expect(
reaction.toJson(),
{
"message_id": "76cd8c82-b557-4e48-9d12-87995d3a0e04",
"type": "wow",
"score": 1,
"bananas": 'yes',
'message_id': '76cd8c82-b557-4e48-9d12-87995d3a0e04',
'type': 'wow',
'score': 1,
'bananas': 'yes',
},
);
});
@@ -26,13 +26,13 @@ void main() {
test('should serialize to json correctly', () {
final read = Read(
lastRead: DateTime.parse('2020-01-28T22:17:30.966485504Z'),
user: User.init('bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'),
user: User(id: 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'),
unreadMessages: 10,
);
expect(read.toJson(), {
"user": {"id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e"},
"last_read": "2020-01-28T22:17:30.966485Z",
'user': {'id': 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'},
'last_read': '2020-01-28T22:17:30.966485Z',
'unread_messages': 10,
});
});
@@ -49,12 +49,12 @@ void main() {
});
test('should return null', () {
final result = Serialization.moveToExtraDataFromRoot(null, [
final result = Serialization.moveToExtraDataFromRoot({}, [
'prop1',
'prop2',
]);
expect(result, null);
expect(result, {'extra_data': {}});
});
});
}
@@ -1,13 +1,14 @@
import 'dart:convert';
import 'package:test/test.dart';
import 'package:stream_chat/src/models/user.dart';
import 'package:test/test.dart';
void main() {
group('src/models/user', () {
const jsonExample = '''
{
"id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e"
"id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e",
"role": "test-role"
}
''';
@@ -17,11 +18,13 @@ void main() {
});
test('should serialize to json correctly', () {
final user =
User(id: 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e', role: "abc");
final user = User(
id: 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e',
role: 'abc',
);
expect(user.toJson(), {
'id': "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e",
'id': 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e',
});
});
});
+7 -7
View File
@@ -1,8 +1,7 @@
import 'dart:io';
import 'package:rxdart/rxdart.dart';
import 'package:test/test.dart';
import 'package:stream_chat/version.dart';
import 'package:test/test.dart';
void prepareTest() {
// https://github.com/flutter/flutter/issues/20907
@@ -14,11 +13,12 @@ void prepareTest() {
void main() {
prepareTest();
test('stream chat version matches pubspec', () {
final String pubspecPath = '${Directory.current.path}/pubspec.yaml';
final String pubspec = File(pubspecPath).readAsStringSync();
final RegExp regex = RegExp('version:\s*(.*)');
final RegExpMatch match = regex.firstMatch(pubspec);
final pubspecPath = '${Directory.current.path}/pubspec.yaml';
final pubspec = File(pubspecPath).readAsStringSync();
// ignore: unnecessary_string_escapes
final regex = RegExp('version:\s*(.*)');
final match = regex.firstMatch(pubspec);
expect(match, isNotNull);
expect(PACKAGE_VERSION, match.group(1).trim());
expect(PACKAGE_VERSION, match?.group(1)?.trim());
});
}
@@ -1,3 +1,8 @@
## 2.0.0-nullsafety.0
- Migrate this package to null safety
- Updated `stream_chat_core` dependency
## 1.5.3
- Updated `stream_chat_core` dependency
@@ -6,6 +6,7 @@ analyzer:
exclude:
- lib/**/*.g.dart
- example/**
- lib/src/emoji
linter:
rules:
@@ -1 +0,0 @@
/Users/salvatoregiordano/fvm/versions/beta
@@ -1,3 +0,0 @@
{
"flutterSdkVersion": "beta"
}
@@ -61,8 +61,8 @@ class MyApp extends StatelessWidget {
themeMode: ThemeMode.system,
builder: (context, widget) {
return StreamChat(
child: widget,
client: client,
child: widget,
);
},
home: StreamChannel(
@@ -80,7 +80,7 @@ class MyApp extends StatelessWidget {
class ChannelPage extends StatelessWidget {
/// Creates the page that shows the list of messages
const ChannelPage({
Key key,
Key? key,
}) : super(key: key);
@override
@@ -25,8 +25,8 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) => StreamChat(
child: child,
client: client,
child: child,
),
home: SplitView(),
);
@@ -39,7 +39,7 @@ class SplitView extends StatefulWidget {
}
class _SplitViewState extends State<SplitView> {
Channel selectedChannel;
Channel? selectedChannel;
@override
Widget build(BuildContext context) {
@@ -47,6 +47,7 @@ class _SplitViewState extends State<SplitView> {
direction: Axis.horizontal,
children: <Widget>[
Flexible(
flex: 1,
child: ChannelListPage(
onTap: (channel) {
setState(() {
@@ -54,15 +55,15 @@ class _SplitViewState extends State<SplitView> {
});
},
),
flex: 1,
),
Flexible(
flex: 2,
child: Scaffold(
body: selectedChannel != null
? StreamChannel(
key: ValueKey(selectedChannel.cid),
key: ValueKey(selectedChannel!.cid),
channel: selectedChannel!,
child: ChannelPage(),
channel: selectedChannel,
)
: Center(
child: Text(
@@ -71,7 +72,6 @@ class _SplitViewState extends State<SplitView> {
),
),
),
flex: 2,
),
],
);
@@ -79,7 +79,7 @@ class _SplitViewState extends State<SplitView> {
}
class ChannelListPage extends StatelessWidget {
final void Function(Channel) onTap;
final void Function(Channel)? onTap;
ChannelListPage({this.onTap});
@@ -90,14 +90,13 @@ class ChannelListPage extends StatelessWidget {
child: ChannelListView(
onChannelTap: onTap != null
? (channel, _) {
onTap(channel);
onTap!(channel);
}
: null,
filter: {
'members': {
'\$in': [StreamChat.of(context).user.id],
}
},
filter: Filter.in_(
'members',
[StreamChat.of(context).user!.id],
),
sort: [SortOption('last_message_at')],
pagination: PaginationParams(
limit: 20,
@@ -110,7 +109,7 @@ class ChannelListPage extends StatelessWidget {
class ChannelPage extends StatelessWidget {
const ChannelPage({
Key key,
Key? key,
}) : super(key: key);
@override
@@ -55,8 +55,8 @@ class MyApp extends StatelessWidget {
return MaterialApp(
builder: (context, widget) {
return StreamChat(
child: widget,
client: client,
child: widget,
);
},
home: StreamChannel(
@@ -69,7 +69,7 @@ class MyApp extends StatelessWidget {
class ChannelPage extends StatelessWidget {
const ChannelPage({
Key key,
Key? key,
}) : super(key: key);
@override
@@ -56,11 +56,10 @@ class ChannelListPage extends StatelessWidget {
return Scaffold(
body: ChannelsBloc(
child: ChannelListView(
filter: {
// 'members': {
// '\$in': [StreamChat.of(context).user.id],
// }
},
filter: Filter.in_(
'members',
[StreamChat.of(context).user.id],
),
sort: [SortOption('last_message_at')],
pagination: PaginationParams(
limit: 20,
@@ -74,7 +73,7 @@ class ChannelListPage extends StatelessWidget {
class ChannelPage extends StatelessWidget {
const ChannelPage({
Key key,
Key? key,
}) : super(key: key);
@override
@@ -1,4 +1,5 @@
// ignore_for_file: public_member_api_docs
import 'package:collection/collection.dart' show IterableExtension;
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
@@ -43,8 +44,8 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) => StreamChat(
child: child,
client: client,
child: child,
),
home: ChannelListPage(),
);
@@ -57,11 +58,10 @@ class ChannelListPage extends StatelessWidget {
return Scaffold(
body: ChannelsBloc(
child: ChannelListView(
filter: {
'members': {
'\$in': [StreamChat.of(context).user.id],
}
},
filter: Filter.in_(
'members',
[StreamChat.of(context).user!.id],
),
channelPreviewBuilder: _channelPreviewBuilder,
// sort: [SortOption('last_message_at')],
pagination: PaginationParams(
@@ -74,13 +74,12 @@ class ChannelListPage extends StatelessWidget {
}
Widget _channelPreviewBuilder(BuildContext context, Channel channel) {
final lastMessage = channel.state.messages.reversed.firstWhere(
final lastMessage = channel.state?.messages.reversed.firstWhereOrNull(
(message) => !message.isDeleted,
orElse: () => null,
);
final subtitle = (lastMessage == null ? 'nothing yet' : lastMessage.text);
final opacity = channel.state.unreadCount > .0 ? 1.0 : 0.5;
final subtitle = (lastMessage == null ? 'nothing yet' : lastMessage.text!);
final opacity = (channel.state?.unreadCount ?? 0) > 0 ? 1.0 : 0.5;
return ListTile(
onTap: () {
@@ -88,8 +87,8 @@ class ChannelListPage extends StatelessWidget {
context,
MaterialPageRoute(
builder: (_) => StreamChannel(
child: ChannelPage(),
channel: channel,
child: ChannelPage(),
),
),
);
@@ -99,7 +98,7 @@ class ChannelListPage extends StatelessWidget {
),
title: ChannelName(
textStyle:
StreamChatTheme.of(context).channelPreviewTheme.title.copyWith(
StreamChatTheme.of(context).channelPreviewTheme.title!.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.black
@@ -107,10 +106,10 @@ class ChannelListPage extends StatelessWidget {
),
),
subtitle: Text(subtitle),
trailing: channel.state.unreadCount > 0
trailing: channel.state!.unreadCount! > 0
? CircleAvatar(
radius: 10,
child: Text(channel.state.unreadCount.toString()),
child: Text(channel.state!.unreadCount.toString()),
)
: SizedBox(),
);
@@ -119,7 +118,7 @@ class ChannelListPage extends StatelessWidget {
class ChannelPage extends StatelessWidget {
const ChannelPage({
Key key,
Key? key,
}) : super(key: key);
@override
@@ -33,8 +33,8 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) => StreamChat(
child: child,
client: client,
child: child,
),
home: Container(
child: ChannelListPage(),
@@ -49,11 +49,10 @@ class ChannelListPage extends StatelessWidget {
return Scaffold(
body: ChannelsBloc(
child: ChannelListView(
filter: {
'members': {
'\$in': [StreamChat.of(context).user.id],
}
},
filter: Filter.in_(
'members',
[StreamChat.of(context).user!.id],
),
sort: [SortOption('last_message_at')],
pagination: PaginationParams(
limit: 20,
@@ -67,7 +66,7 @@ class ChannelListPage extends StatelessWidget {
class ChannelPage extends StatelessWidget {
const ChannelPage({
Key key,
Key? key,
}) : super(key: key);
@override
@@ -93,10 +92,10 @@ class ChannelPage extends StatelessWidget {
}
class ThreadPage extends StatelessWidget {
final Message parent;
final Message? parent;
ThreadPage({
Key key,
Key? key,
this.parent,
}) : super(key: key);
@@ -104,7 +103,7 @@ class ThreadPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: ThreadHeader(
parent: parent,
parent: parent!,
),
body: Column(
children: <Widget>[
@@ -38,8 +38,8 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) => StreamChat(
child: child,
client: client,
child: child,
),
home: ChannelListPage(),
);
@@ -52,11 +52,10 @@ class ChannelListPage extends StatelessWidget {
return Scaffold(
body: ChannelsBloc(
child: ChannelListView(
filter: {
'members': {
'\$in': [StreamChat.of(context).user.id],
}
},
filter: Filter.in_(
'members',
[StreamChat.of(context).user!.id],
),
sort: [SortOption('last_message_at')],
pagination: PaginationParams(
limit: 20,
@@ -70,7 +69,7 @@ class ChannelListPage extends StatelessWidget {
class ChannelPage extends StatelessWidget {
const ChannelPage({
Key key,
Key? key,
}) : super(key: key);
@override
@@ -96,7 +95,7 @@ class ChannelPage extends StatelessWidget {
List<Message> messages,
) {
final message = details.message;
final isCurrentUser = StreamChat.of(context).user.id == message.user.id;
final isCurrentUser = StreamChat.of(context).user!.id == message.user!.id;
final textAlign = isCurrentUser ? TextAlign.right : TextAlign.left;
final color = isCurrentUser ? Colors.blueGrey : Colors.blue;
@@ -111,11 +110,11 @@ class ChannelPage extends StatelessWidget {
),
child: ListTile(
title: Text(
message.text,
message.text!,
textAlign: textAlign,
),
subtitle: Text(
message.user.extraData['name'],
message.user!.extraData['name'] as String,
textAlign: textAlign,
),
),
@@ -64,9 +64,9 @@ class MyApp extends StatelessWidget {
theme: themeData,
builder: (context, child) {
return StreamChat(
child: child,
client: client,
streamChatThemeData: customTheme,
child: child,
);
},
home: ChannelListPage(),
@@ -80,11 +80,10 @@ class ChannelListPage extends StatelessWidget {
return Scaffold(
body: ChannelsBloc(
child: ChannelListView(
filter: {
'members': {
'\$in': [StreamChat.of(context).user.id],
}
},
filter: Filter.in_(
'members',
[StreamChat.of(context).user!.id],
),
sort: [SortOption('last_message_at')],
pagination: PaginationParams(
limit: 20,
@@ -98,7 +97,7 @@ class ChannelListPage extends StatelessWidget {
class ChannelPage extends StatelessWidget {
const ChannelPage({
Key key,
Key? key,
}) : super(key: key);
@override
@@ -124,10 +123,10 @@ class ChannelPage extends StatelessWidget {
}
class ThreadPage extends StatelessWidget {
final Message parent;
final Message? parent;
ThreadPage({
Key key,
Key? key,
this.parent,
}) : super(key: key);
@@ -135,7 +134,7 @@ class ThreadPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: ThreadHeader(
parent: parent,
parent: parent!,
),
body: Column(
children: <Widget>[
@@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'
dependencies:
flutter:
@@ -26,22 +26,12 @@ dependencies:
stream_chat_flutter:
path: ../
stream_chat_persistence:
git:
url: https://github.com/GetStream/stream-chat-flutter.git
ref: develop
path: packages/stream_chat_persistence
path: ../../stream_chat_persistence
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.0
dependency_overrides:
stream_chat:
path: ../../stream_chat
stream_chat_flutter_core:
path: ../../stream_chat_flutter_core
stream_chat_persistence:
path: ../../stream_chat_persistence
cupertino_icons: ^1.0.2
collection: ^1.15.0
dev_dependencies:
flutter_test:
@@ -6,9 +6,9 @@ import '../utils.dart';
class AttachmentTitle extends StatelessWidget {
const AttachmentTitle({
Key key,
@required this.attachment,
@required this.messageTheme,
Key? key,
required this.attachment,
required this.messageTheme,
}) : super(key: key);
final MessageTheme messageTheme;
@@ -19,7 +19,7 @@ class AttachmentTitle extends StatelessWidget {
return GestureDetector(
onTap: () {
if (attachment.titleLink != null) {
launchURL(context, attachment.titleLink);
launchURL(context, attachment.titleLink!);
}
},
child: Padding(
@@ -28,17 +28,18 @@ class AttachmentTitle extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
attachment.title,
overflow: TextOverflow.ellipsis,
style: messageTheme.messageText.copyWith(
color: StreamChatTheme.of(context).colorTheme.accentBlue,
fontWeight: FontWeight.bold,
if (attachment.title != null)
Text(
attachment.title!,
overflow: TextOverflow.ellipsis,
style: messageTheme.messageText?.copyWith(
color: StreamChatTheme.of(context).colorTheme.accentBlue,
fontWeight: FontWeight.bold,
),
),
),
if (attachment.titleLink != null || attachment.ogScrapeUrl != null)
Text(
Uri.parse(attachment.titleLink ?? attachment.ogScrapeUrl)
Uri.parse(attachment.titleLink ?? attachment.ogScrapeUrl!)
.authority
.split('.')
.reversed

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