Merge branch 'develop' into hotfix/ownReactions

This commit is contained in:
Salvatore Giordano
2022-02-01 09:54:57 +01:00
103 changed files with 696 additions and 363 deletions
+3 -2
View File
@@ -1,8 +1,9 @@
name: Dart Code Metrics name: Dart Code Metrics
env: env:
flutter_version: "2.5.0" flutter_version: "2.8.1"
folders: "lib, test" folders: "lib, test"
melos_version: "1.2.0"
on: on:
pull_request: pull_request:
@@ -33,7 +34,7 @@ jobs:
flutter-version: ${{ env.flutter_version }} flutter-version: ${{ env.flutter_version }}
- name: "Install Tools" - name: "Install Tools"
run: flutter pub global activate melos 1.0.0-dev.10 run: flutter pub global activate melos ${{ env.melos_version }}
- name: "Bootstrap Workspace" - name: "Bootstrap Workspace"
run: melos bootstrap run: melos bootstrap
@@ -2,8 +2,8 @@ name: stream_flutter_workflow
env: env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
flutter_version: "2.5.1" flutter_version: "2.8.1"
melos_version: "1.0.0-dev.10" melos_version: "1.2.0"
on: on:
pull_request: pull_request:
+15 -1
View File
@@ -1,13 +1,27 @@
## Upcoming ## Upcoming
🐞 Fixed
- [[#857]](https://github.com/GetStream/stream-chat-flutter/issues/857) Channel now listens for member ban/unban and
updates the channel state with the latest data.
- [[#748]](https://github.com/GetStream/stream-chat-flutter/issues/748) `Message.user` are now also included while saving users in persistence.
🔄 Changed 🔄 Changed
- `client.location` is now deprecated in favor of the new [edge server](https://getstream.io/blog/chat-edge-infrastructure) and will be removed in v4.0.0. - `client.location` is now deprecated in favor of the
new [edge server](https://getstream.io/blog/chat-edge-infrastructure) and will be removed in v4.0.0.
- `channel.banUser`, `channel.unbanUser` is now deprecated in favor of the new `channel.banMember`
and `channel.unbanMember` and will be removed in v4.0.0.
✅ Added ✅ Added
- Added `client.enrichUrl` endpoint for enriching URLs with metadata. - Added `client.enrichUrl` endpoint for enriching URLs with metadata.
- Fixed `unreadCount` after removing user from a channel. - Fixed `unreadCount` after removing user from a channel.
- Added `client.queryBannedUsers`, `channel.queryBannedUsers` endpoint for querying banned users.
🐞 Fixed
- [[#871]](https://github.com/GetStream/stream-chat-flutter/issues/871) Fixed thread message deletion.
🐞 Fixed 🐞 Fixed
@@ -4,15 +4,9 @@ import 'dart:math';
import 'package:collection/collection.dart' import 'package:collection/collection.dart'
show IterableExtension, ListEquality; show IterableExtension, ListEquality;
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:rate_limiter/rate_limiter.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/src/client/retry_queue.dart'; import 'package:stream_chat/src/client/retry_queue.dart';
import 'package:stream_chat/src/core/error/error.dart';
import 'package:stream_chat/src/core/models/attachment_file.dart';
import 'package:stream_chat/src/core/models/channel_state.dart';
import 'package:stream_chat/src/core/models/user.dart';
import 'package:stream_chat/src/core/util/utils.dart'; import 'package:stream_chat/src/core/util/utils.dart';
import 'package:stream_chat/src/event_type.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
/// Class that manages a specific channel. /// Class that manages a specific channel.
@@ -1276,6 +1270,21 @@ class Channel {
pagination: pagination, pagination: pagination,
); );
/// Query channel banned users.
Future<QueryBannedUsersResponse> queryBannedUsers({
Filter? filter,
List<SortOption>? sort,
PaginationParams? pagination,
}) {
_checkInitialized();
filter ??= Filter.equal('channel_cid', cid!);
return _client.queryBannedUsers(
filter: filter,
sort: sort,
pagination: pagination,
);
}
/// Mutes the channel. /// Mutes the channel.
Future<EmptyResponse> mute({Duration? expiration}) { Future<EmptyResponse> mute({Duration? expiration}) {
_checkInitialized(); _checkInitialized();
@@ -1289,9 +1298,17 @@ class Channel {
} }
/// Bans the user with given [userID] from the channel. /// Bans the user with given [userID] from the channel.
@Deprecated("Use 'banMember' instead")
Future<EmptyResponse> banUser( Future<EmptyResponse> banUser(
String userID, String userID,
Map<String, dynamic> options, Map<String, dynamic> options,
) =>
banMember(userID, options);
/// Bans the member with given [userID] from the channel.
Future<EmptyResponse> banMember(
String userID,
Map<String, dynamic> options,
) async { ) async {
_checkInitialized(); _checkInitialized();
final opts = Map<String, dynamic>.from(options) final opts = Map<String, dynamic>.from(options)
@@ -1303,7 +1320,11 @@ class Channel {
} }
/// Remove the ban for the user with given [userID] in the channel. /// Remove the ban for the user with given [userID] in the channel.
Future<EmptyResponse> unbanUser(String userID) async { @Deprecated("Use 'unbanMember' instead")
Future<EmptyResponse> unbanUser(String userID) => unbanMember(userID);
/// Remove the ban for the member with given [userID] in the channel.
Future<EmptyResponse> unbanMember(String userID) async {
_checkInitialized(); _checkInitialized();
return _client.unbanUser(userID, { return _client.unbanUser(userID, {
'type': type, 'type': type,
@@ -1474,6 +1495,10 @@ class ChannelClientState {
_listenMemberRemoved(); _listenMemberRemoved();
_listenMemberBanned();
_listenMemberUnbanned();
_startCleaning(); _startCleaning();
_startCleaningPinnedMessages(); _startCleaningPinnedMessages();
@@ -1572,6 +1597,54 @@ class ChannelClientState {
})); }));
} }
void _listenMemberBanned() {
_subscriptions.add(_channel
.on(EventType.userBanned)
.where((it) => it.cid != null) // filters channel ban from app ban
.listen(
(event) async {
final user = event.user!;
final member = await _channel
.queryMembers(filter: Filter.equal('id', user.id))
.then((it) => it.members.first);
_updateMember(member);
},
));
}
void _listenMemberUnbanned() {
_subscriptions.add(_channel
.on(EventType.userUnbanned)
.where((it) => it.cid != null) // filters channel ban from app ban
.listen(
(event) async {
final user = event.user!;
final member = await _channel
.queryMembers(filter: Filter.equal('id', user.id))
.then((it) => it.members.first);
_updateMember(member);
},
));
}
void _updateMember(Member member) {
final currentMembers = [...members];
final memberIndex = currentMembers.indexWhere(
(m) => m.userId == member.userId,
);
if (memberIndex == -1) return;
currentMembers[memberIndex] = member;
updateChannelState(
channelState.copyWith(
members: currentMembers,
),
);
}
/// Flag which indicates if [ChannelClientState] contain latest/recent messages or not. /// Flag which indicates if [ChannelClientState] contain latest/recent messages or not.
/// ///
/// This flag should be managed by UI sdks. /// This flag should be managed by UI sdks.
@@ -1748,7 +1821,13 @@ class ChannelClientState {
if (replyCount == null || replyCount == 0) return; if (replyCount == null || replyCount == 0) return;
addMessage(parentMessage.copyWith(replyCount: replyCount - 1)); addMessage(parentMessage.copyWith(replyCount: replyCount - 1));
updateThreadInfo(parentId, threads[parentId]!..remove(message)); updateThreadInfo(
parentId,
threads[parentId]!
..removeWhere(
(e) => e.id == message.id,
),
);
} else { } else {
// Remove regular message // Remove regular message
final allMessages = [...messages]; final allMessages = [...messages];
@@ -687,6 +687,18 @@ class StreamChatClient {
return response; return response;
} }
/// Query banned users.
Future<QueryBannedUsersResponse> queryBannedUsers({
required Filter filter,
List<SortOption>? sort,
PaginationParams? pagination,
}) =>
_chatApi.moderation.queryBannedUsers(
filter: filter,
sort: sort,
pagination: pagination,
);
/// A message search. /// A message search.
Future<SearchMessagesResponse> search( Future<SearchMessagesResponse> search(
Filter filter, { Filter filter, {
@@ -1,13 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:logging/logging.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/src/client/channel.dart';
import 'package:stream_chat/src/client/retry_policy.dart'; import 'package:stream_chat/src/client/retry_policy.dart';
import 'package:stream_chat/src/core/error/error.dart';
import 'package:stream_chat/src/core/models/message.dart';
import 'package:stream_chat/src/event_type.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
/// The retry queue associated to a channel /// The retry queue associated to a channel
@@ -1,5 +1,7 @@
import 'package:stream_chat/src/core/api/responses.dart'; import 'dart:convert';
import 'package:stream_chat/src/core/http/stream_http_client.dart'; import 'package:stream_chat/src/core/http/stream_http_client.dart';
import 'package:stream_chat/stream_chat.dart';
/// Defines the api dedicated to moderation operations /// Defines the api dedicated to moderation operations
class ModerationApi { class ModerationApi {
@@ -125,4 +127,24 @@ class ModerationApi {
); );
return EmptyResponse.fromJson(response.data); return EmptyResponse.fromJson(response.data);
} }
/// Queries banned users.
Future<QueryBannedUsersResponse> queryBannedUsers({
Filter? filter,
List<SortOption>? sort,
PaginationParams? pagination,
}) async {
final response = await _client.get(
'/query_banned_users',
queryParameters: {
'payload': jsonEncode({
if (sort != null) 'sort': sort,
if (filter != null) 'filter_conditions': filter,
if (pagination != null) ...pagination.toJson(),
}),
},
);
return QueryBannedUsersResponse.fromJson(response.data);
}
} }
@@ -1,6 +1,7 @@
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:stream_chat/src/client/client.dart'; import 'package:stream_chat/src/client/client.dart';
import 'package:stream_chat/src/core/error/error.dart'; import 'package:stream_chat/src/core/error/error.dart';
import 'package:stream_chat/src/core/models/banned_user.dart';
import 'package:stream_chat/src/core/models/channel_model.dart'; import 'package:stream_chat/src/core/models/channel_model.dart';
import 'package:stream_chat/src/core/models/channel_state.dart'; import 'package:stream_chat/src/core/models/channel_state.dart';
import 'package:stream_chat/src/core/models/device.dart'; import 'package:stream_chat/src/core/models/device.dart';
@@ -106,6 +107,18 @@ class QueryUsersResponse extends _BaseResponse {
_$QueryUsersResponseFromJson(json); _$QueryUsersResponseFromJson(json);
} }
/// Model response for [StreamChatClient.queryBannedUsers] api call
@JsonSerializable(createToJson: false)
class QueryBannedUsersResponse extends _BaseResponse {
/// List of users returned by the query
@JsonKey(defaultValue: [])
late List<BannedUser> bans;
/// Create a new instance from a json
static QueryBannedUsersResponse fromJson(Map<String, dynamic> json) =>
_$QueryBannedUsersResponseFromJson(json);
}
/// Model response for [channel.getReactions] api call /// Model response for [channel.getReactions] api call
@JsonSerializable(createToJson: false) @JsonSerializable(createToJson: false)
class QueryReactionsResponse extends _BaseResponse { class QueryReactionsResponse extends _BaseResponse {
@@ -62,6 +62,15 @@ QueryUsersResponse _$QueryUsersResponseFromJson(Map<String, dynamic> json) =>
.toList() ?? .toList() ??
[]; [];
QueryBannedUsersResponse _$QueryBannedUsersResponseFromJson(
Map<String, dynamic> json) =>
QueryBannedUsersResponse()
..duration = json['duration'] as String?
..bans = (json['bans'] as List<dynamic>?)
?.map((e) => BannedUser.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
QueryReactionsResponse _$QueryReactionsResponseFromJson( QueryReactionsResponse _$QueryReactionsResponseFromJson(
Map<String, dynamic> json) => Map<String, dynamic> json) =>
QueryReactionsResponse() QueryReactionsResponse()
@@ -1,5 +1,4 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:stream_chat/src/core/error/chat_error_code.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:web_socket_channel/web_socket_channel.dart';
@@ -3,7 +3,6 @@ import 'dart:typed_data';
import 'package:dio/dio.dart' show MultipartFile; import 'package:dio/dio.dart' show MultipartFile;
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:http_parser/http_parser.dart'; import 'package:http_parser/http_parser.dart';
import 'package:meta/meta.dart';
import 'package:stream_chat/src/core/platform_detector/platform_detector.dart'; import 'package:stream_chat/src/core/platform_detector/platform_detector.dart';
import 'package:stream_chat/src/core/util/extension.dart'; import 'package:stream_chat/src/core/util/extension.dart';
@@ -0,0 +1,80 @@
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:stream_chat/src/core/models/channel_model.dart';
import 'package:stream_chat/src/core/models/user.dart';
part 'banned_user.g.dart';
/// Contains information about a [User] that was banned from a [Channel] or App.
@JsonSerializable()
class BannedUser extends Equatable {
/// Creates a new instance of [BannedUser]
const BannedUser({
required this.user,
this.bannedBy,
this.channel,
this.createdAt,
this.expires,
this.shadow = false,
this.reason,
});
/// Create a new instance from a json
factory BannedUser.fromJson(Map<String, dynamic> json) =>
_$BannedUserFromJson(json);
/// Banned user.
final User user;
/// User that banned the [user].
final User? bannedBy;
/// Channel where the [user] was banned.
final ChannelModel? channel;
/// Timestamp when the [user] was banned.
final DateTime? createdAt;
/// Timestamp when the [user] will be unbanned.
final DateTime? expires;
/// Whether the [user] is a shadow banned user.
final bool shadow;
/// Reason for the ban.
final String? reason;
/// Serialize to json
Map<String, dynamic> toJson() => _$BannedUserToJson(this);
/// Returns a copy of this object with the given fields updated.
BannedUser copyWith({
User? user,
User? bannedBy,
ChannelModel? channel,
DateTime? createdAt,
DateTime? expires,
bool? shadow,
String? reason,
}) =>
BannedUser(
user: user ?? this.user,
bannedBy: bannedBy ?? this.bannedBy,
channel: channel ?? this.channel,
createdAt: createdAt ?? this.createdAt,
expires: expires ?? this.expires,
shadow: shadow ?? this.shadow,
reason: reason ?? this.reason,
);
@override
List<Object?> get props => [
user,
bannedBy,
channel,
createdAt,
expires,
shadow,
reason,
];
}
@@ -0,0 +1,36 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'banned_user.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
BannedUser _$BannedUserFromJson(Map<String, dynamic> json) => BannedUser(
user: User.fromJson(json['user'] as Map<String, dynamic>),
bannedBy: json['banned_by'] == null
? null
: User.fromJson(json['banned_by'] as Map<String, dynamic>),
channel: json['channel'] == null
? null
: ChannelModel.fromJson(json['channel'] as Map<String, dynamic>),
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
expires: json['expires'] == null
? null
: DateTime.parse(json['expires'] as String),
shadow: json['shadow'] as bool? ?? false,
reason: json['reason'] as String?,
);
Map<String, dynamic> _$BannedUserToJson(BannedUser instance) =>
<String, dynamic>{
'user': instance.user.toJson(),
'banned_by': instance.bannedBy?.toJson(),
'channel': instance.channel?.toJson(),
'created_at': instance.createdAt?.toIso8601String(),
'expires': instance.expires?.toIso8601String(),
'shadow': instance.shadow,
'reason': instance.reason,
};
@@ -20,6 +20,7 @@ class Member extends Equatable {
DateTime? createdAt, DateTime? createdAt,
DateTime? updatedAt, DateTime? updatedAt,
this.banned = false, this.banned = false,
this.banExpires,
this.shadowBanned = false, this.shadowBanned = false,
}) : createdAt = createdAt ?? DateTime.now(), }) : createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now(); updatedAt = updatedAt ?? DateTime.now();
@@ -56,6 +57,9 @@ class Member extends Equatable {
/// True if the member is banned from the channel /// True if the member is banned from the channel
final bool banned; final bool banned;
/// The date at which the ban will expire.
final DateTime? banExpires;
/// True if the member is shadow banned from the channel /// True if the member is shadow banned from the channel
final bool shadowBanned; final bool shadowBanned;
@@ -77,6 +81,7 @@ class Member extends Equatable {
DateTime? createdAt, DateTime? createdAt,
DateTime? updatedAt, DateTime? updatedAt,
bool? banned, bool? banned,
DateTime? banExpires,
bool? shadowBanned, bool? shadowBanned,
}) => }) =>
Member( Member(
@@ -85,6 +90,7 @@ class Member extends Equatable {
inviteRejectedAt: inviteRejectedAt ?? this.inviteRejectedAt, inviteRejectedAt: inviteRejectedAt ?? this.inviteRejectedAt,
invited: invited ?? this.invited, invited: invited ?? this.invited,
banned: banned ?? this.banned, banned: banned ?? this.banned,
banExpires: banExpires ?? this.banExpires,
shadowBanned: shadowBanned ?? this.shadowBanned, shadowBanned: shadowBanned ?? this.shadowBanned,
role: role ?? this.role, role: role ?? this.role,
userId: userId ?? this.userId, userId: userId ?? this.userId,
@@ -106,6 +112,7 @@ class Member extends Equatable {
userId, userId,
isModerator, isModerator,
banned, banned,
banExpires,
shadowBanned, shadowBanned,
createdAt, createdAt,
updatedAt, updatedAt,
@@ -27,6 +27,9 @@ Member _$MemberFromJson(Map<String, dynamic> json) => Member(
? null ? null
: DateTime.parse(json['updated_at'] as String), : DateTime.parse(json['updated_at'] as String),
banned: json['banned'] as bool? ?? false, banned: json['banned'] as bool? ?? false,
banExpires: json['ban_expires'] == null
? null
: DateTime.parse(json['ban_expires'] as String),
shadowBanned: json['shadow_banned'] as bool? ?? false, shadowBanned: json['shadow_banned'] as bool? ?? false,
); );
@@ -39,6 +42,7 @@ Map<String, dynamic> _$MemberToJson(Member instance) => <String, dynamic>{
'user_id': instance.userId, 'user_id': instance.userId,
'is_moderator': instance.isModerator, 'is_moderator': instance.isModerator,
'banned': instance.banned, 'banned': instance.banned,
'ban_expires': instance.banExpires?.toIso8601String(),
'shadow_banned': instance.shadowBanned, 'shadow_banned': instance.shadowBanned,
'created_at': instance.createdAt.toIso8601String(), 'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(), 'updated_at': instance.updatedAt.toIso8601String(),
@@ -26,6 +26,7 @@ class OwnUser extends User {
bool online = false, bool online = false,
Map<String, Object?> extraData = const {}, Map<String, Object?> extraData = const {},
bool banned = false, bool banned = false,
DateTime? banExpires,
List<String> teams = const [], List<String> teams = const [],
String? language, String? language,
}) : super( }) : super(
@@ -39,6 +40,7 @@ class OwnUser extends User {
online: online, online: online,
extraData: extraData, extraData: extraData,
banned: banned, banned: banned,
banExpires: banExpires,
teams: teams, teams: teams,
language: language, language: language,
); );
@@ -75,6 +77,7 @@ class OwnUser extends User {
bool? online, bool? online,
Map<String, Object?>? extraData, Map<String, Object?>? extraData,
bool? banned, bool? banned,
DateTime? banExpires,
List<String>? teams, List<String>? teams,
List<Mute>? channelMutes, List<Mute>? channelMutes,
List<Device>? devices, List<Device>? devices,
@@ -91,6 +94,7 @@ class OwnUser extends User {
// if null, it will be retrieved from extraData['image'] // if null, it will be retrieved from extraData['image']
image: image, image: image,
banned: banned ?? this.banned, banned: banned ?? this.banned,
banExpires: banExpires ?? this.banExpires,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
lastActive: lastActive ?? this.lastActive, lastActive: lastActive ?? this.lastActive,
@@ -35,6 +35,9 @@ OwnUser _$OwnUserFromJson(Map<String, dynamic> json) => OwnUser(
online: json['online'] as bool? ?? false, online: json['online'] as bool? ?? false,
extraData: json['extra_data'] as Map<String, dynamic>? ?? const {}, extraData: json['extra_data'] as Map<String, dynamic>? ?? const {},
banned: json['banned'] as bool? ?? false, banned: json['banned'] as bool? ?? false,
banExpires: json['ban_expires'] == null
? null
: DateTime.parse(json['ban_expires'] as String),
teams: teams:
(json['teams'] as List<dynamic>?)?.map((e) => e as String).toList() ?? (json['teams'] as List<dynamic>?)?.map((e) => e as String).toList() ??
const [], const [],
@@ -41,6 +41,7 @@ class User extends Equatable {
Map<String, Object?> extraData = const {}, Map<String, Object?> extraData = const {},
this.online = false, this.online = false,
this.banned = false, this.banned = false,
this.banExpires,
this.teams = const [], this.teams = const [],
this.language, this.language,
}) : createdAt = createdAt ?? DateTime.now(), }) : createdAt = createdAt ?? DateTime.now(),
@@ -67,6 +68,8 @@ class User extends Equatable {
'last_active', 'last_active',
'online', 'online',
'banned', 'banned',
'ban_expires',
'dashboard_ban_channel_cid',
'teams', 'teams',
'language', 'language',
]; ];
@@ -129,14 +132,18 @@ class User extends Equatable {
) )
final bool banned; final bool banned;
/// Map of custom user extraData. /// The date at which the ban will expire.
@JsonKey(includeIfNull: false) @JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
final Map<String, Object?> extraData; final DateTime? banExpires;
/// The language this user prefers. /// The language this user prefers.
@JsonKey(includeIfNull: false) @JsonKey(includeIfNull: false)
final String? language; final String? language;
/// Map of custom user extraData.
@JsonKey(includeIfNull: false)
final Map<String, Object?> extraData;
/// List of users to list of userIds. /// List of users to list of userIds.
static List<String>? toIds(List<User>? users) => static List<String>? toIds(List<User>? users) =>
users?.map((u) => u.id).toList(); users?.map((u) => u.id).toList();
@@ -158,6 +165,7 @@ class User extends Equatable {
bool? online, bool? online,
Map<String, Object?>? extraData, Map<String, Object?>? extraData,
bool? banned, bool? banned,
DateTime? banExpires,
List<String>? teams, List<String>? teams,
String? language, String? language,
}) => }) =>
@@ -174,6 +182,7 @@ class User extends Equatable {
online: online ?? this.online, online: online ?? this.online,
extraData: extraData ?? this.extraData, extraData: extraData ?? this.extraData,
banned: banned ?? this.banned, banned: banned ?? this.banned,
banExpires: banExpires ?? this.banExpires,
teams: teams ?? this.teams, teams: teams ?? this.teams,
language: language ?? this.language, language: language ?? this.language,
); );
@@ -186,6 +195,7 @@ class User extends Equatable {
online, online,
extraData, extraData,
banned, banned,
banExpires,
teams, teams,
language, language,
]; ];
@@ -21,6 +21,9 @@ User _$UserFromJson(Map<String, dynamic> json) => User(
extraData: json['extra_data'] as Map<String, dynamic>? ?? const {}, extraData: json['extra_data'] as Map<String, dynamic>? ?? const {},
online: json['online'] as bool? ?? false, online: json['online'] as bool? ?? false,
banned: json['banned'] as bool? ?? false, banned: json['banned'] as bool? ?? false,
banExpires: json['ban_expires'] == null
? null
: DateTime.parse(json['ban_expires'] as String),
teams: teams:
(json['teams'] as List<dynamic>?)?.map((e) => e as String).toList() ?? (json['teams'] as List<dynamic>?)?.map((e) => e as String).toList() ??
const [], const [],
@@ -45,7 +48,8 @@ Map<String, dynamic> _$UserToJson(User instance) {
writeNotNull('last_active', readonly(instance.lastActive)); writeNotNull('last_active', readonly(instance.lastActive));
writeNotNull('online', readonly(instance.online)); writeNotNull('online', readonly(instance.online));
writeNotNull('banned', readonly(instance.banned)); writeNotNull('banned', readonly(instance.banned));
val['extra_data'] = instance.extraData; writeNotNull('ban_expires', readonly(instance.banExpires));
writeNotNull('language', instance.language); writeNotNull('language', instance.language);
val['extra_data'] = instance.extraData;
return val; return val;
} }
@@ -253,6 +253,7 @@ abstract class ChatPersistenceClient {
users.addAll([ users.addAll([
channel.createdBy, channel.createdBy,
...messages.map((it) => it.user),
...reads.map((it) => it.user), ...reads.map((it) => it.user),
...members.map((it) => it.user), ...members.map((it) => it.user),
...reactions.map((it) => it.user), ...reactions.map((it) => it.user),
@@ -73,6 +73,12 @@ class EventType {
/// Event sent when a member is removed to a channel /// Event sent when a member is removed to a channel
static const String memberRemoved = 'member.removed'; static const String memberRemoved = 'member.removed';
/// Event sent when a member is removed to a channel
static const String userBanned = 'user.banned';
/// Event sent when a member is removed to a channel
static const String userUnbanned = 'user.unbanned';
/// Event sent when a channel is hidden /// Event sent when a channel is hidden
static const String channelHidden = 'channel.hidden'; static const String channelHidden = 'channel.hidden';
@@ -1,6 +1,6 @@
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:stream_chat/src/client/channel.dart';
import 'package:stream_chat/src/client/retry_policy.dart'; import 'package:stream_chat/src/client/retry_policy.dart';
import 'package:stream_chat/src/core/models/banned_user.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
@@ -1963,6 +1963,35 @@ void main() {
)).called(1); )).called(1);
}); });
test('`.queryBannedUsers`', () async {
final filter = Filter.equal('channel_cid', channelCid);
final bans = List.generate(
3,
(index) => BannedUser(
user: User(id: 'test-user-id-$index'),
bannedBy: User(id: 'test-user-id-${index + 1}'),
),
);
when(() => client.queryBannedUsers(
filter: filter,
sort: any(named: 'sort'),
pagination: any(named: 'pagination'),
)).thenAnswer((_) async => QueryBannedUsersResponse()..bans = bans);
final res = await channel.queryBannedUsers();
expect(res, isNotNull);
expect(res.bans.length, bans.length);
verify(() => client.queryBannedUsers(
filter: filter,
sort: any(named: 'sort'),
pagination: any(named: 'pagination'),
)).called(1);
});
test('`.mute`', () async { test('`.mute`', () async {
when(() => client.muteChannel( when(() => client.muteChannel(
channelCid, channelCid,
@@ -2046,7 +2075,7 @@ void main() {
{'type': channelType, 'id': channelId, ...options}, {'type': channelType, 'id': channelId, ...options},
)).thenAnswer((_) async => EmptyResponse()); )).thenAnswer((_) async => EmptyResponse());
final res = await channel.banUser(userId, options); final res = await channel.banMember(userId, options);
expect(res, isNotNull); expect(res, isNotNull);
@@ -2062,7 +2091,7 @@ void main() {
when(() => client.unbanUser(userId, any())) when(() => client.unbanUser(userId, any()))
.thenAnswer((_) async => EmptyResponse()); .thenAnswer((_) async => EmptyResponse());
final res = await channel.unbanUser(userId); final res = await channel.unbanMember(userId);
expect(res, isNotNull); expect(res, isNotNull);
@@ -1,6 +1,7 @@
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:stream_chat/src/core/api/device_api.dart'; import 'package:stream_chat/src/core/api/device_api.dart';
import 'package:stream_chat/src/core/http/token.dart'; import 'package:stream_chat/src/core/http/token.dart';
import 'package:stream_chat/src/core/models/banned_user.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
@@ -982,6 +983,36 @@ void main() {
verifyNoMoreInteractions(api.user); verifyNoMoreInteractions(api.user);
}); });
test('`.queryBannedUsers`', () async {
final bans = List.generate(
3,
(index) => BannedUser(
user: User(id: 'test-user-id-$index'),
bannedBy: User(id: 'test-user-id-${index + 1}'),
),
);
const cid = 'message:nice-channel';
final filter = Filter.equal('channel_cid', cid);
when(() => api.moderation.queryBannedUsers(
filter: filter,
sort: any(named: 'sort'),
pagination: any(named: 'pagination'),
)).thenAnswer((_) async => QueryBannedUsersResponse()..bans = bans);
final res = await client.queryBannedUsers(filter: filter);
expect(res, isNotNull);
expect(res.bans.length, bans.length);
verify(() => api.moderation.queryBannedUsers(
filter: filter,
sort: any(named: 'sort'),
pagination: any(named: 'pagination'),
)).called(1);
verifyNoMoreInteractions(api.moderation);
});
test('`.search`', () async { test('`.search`', () async {
const cid = 'test-type:test-id'; const cid = 'test-type:test-id';
final filter = Filter.in_('cid', const [cid]); final filter = Filter.in_('cid', const [cid]);
@@ -4,7 +4,6 @@ import 'package:stream_chat/src/client/retry_queue.dart';
import 'package:stream_chat/src/core/models/event.dart'; import 'package:stream_chat/src/core/models/event.dart';
import 'package:stream_chat/src/core/models/message.dart'; import 'package:stream_chat/src/core/models/message.dart';
import 'package:stream_chat/src/event_type.dart'; import 'package:stream_chat/src/event_type.dart';
import 'package:test/scaffolding.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../mocks.dart'; import '../mocks.dart';
@@ -3,8 +3,6 @@ import 'dart:convert';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:stream_chat/src/core/api/channel_api.dart'; import 'package:stream_chat/src/core/api/channel_api.dart';
import 'package:stream_chat/src/core/models/channel_model.dart';
import 'package:stream_chat/src/core/models/channel_state.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
@@ -1,11 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'package:stream_chat/src/core/api/responses.dart';
import 'package:stream_chat/src/core/models/device.dart';
import 'package:stream_chat/src/core/models/member.dart';
import 'package:stream_chat/src/core/models/message.dart';
import 'package:stream_chat/src/core/models/reaction.dart';
import 'package:stream_chat/src/core/models/read.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
@@ -2,9 +2,7 @@ import 'dart:convert';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:stream_chat/src/core/api/requests.dart';
import 'package:stream_chat/src/core/api/user_api.dart'; import 'package:stream_chat/src/core/api/user_api.dart';
import 'package:stream_chat/src/core/models/filter.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
@@ -1,8 +1,3 @@
import 'package:stream_chat/src/core/models/channel_config.dart';
import 'package:stream_chat/src/core/models/channel_state.dart';
import 'package:stream_chat/src/core/models/command.dart';
import 'package:stream_chat/src/core/models/message.dart';
import 'package:stream_chat/src/core/models/user.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
@@ -1,5 +1,3 @@
import 'package:stream_chat/src/core/models/event.dart';
import 'package:stream_chat/src/core/models/own_user.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
@@ -1,6 +1,4 @@
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:stream_chat/src/core/models/own_user.dart';
import 'package:stream_chat/src/core/models/user.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
-1
View File
@@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:dio/dio.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/src/core/api/channel_api.dart'; import 'package:stream_chat/src/core/api/channel_api.dart';
+4 -3
View File
@@ -2,8 +2,9 @@
🐞 Fixed 🐞 Fixed
- SVG rendering fixes - SVG rendering fixes.
- Use file extension instead of mimeType for downloading files - Use file extension instead of mimeType for downloading files.
- [[#860]](https://github.com/GetStream/stream-chat-flutter/issues/860) CastError while compressing Videos.
✅ Added ✅ Added
@@ -23,7 +24,7 @@
🐞 Fixed 🐞 Fixed
- [[#766]]`AttachmentActionsModal` now has customisation options for actions. - [[#766]](https://github.com/GetStream/stream-chat-flutter/issues/766) `AttachmentActionsModal` now has customisation options for actions.
- Fixed `MessageWidget` null errors associated with `channel.memberCount`. - Fixed `MessageWidget` null errors associated with `channel.memberCount`.
- Fixed adding attachments on web. - Fixed adding attachments on web.
- [[#767]](https://github.com/GetStream/stream-chat-flutter/issues/767): Fix `MessageInput` focus behaviour when sending messages. - [[#767]](https://github.com/GetStream/stream-chat-flutter/issues/767): Fix `MessageInput` focus behaviour when sending messages.
@@ -1,5 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_localizations/stream_chat_localizations.dart'; import 'package:stream_chat_localizations/stream_chat_localizations.dart';
@@ -1,7 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Title for attachments /// Title for attachments
class AttachmentTitle extends StatelessWidget { class AttachmentTitle extends StatelessWidget {
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Enum for identifying type of attachment /// Enum for identifying type of attachment
enum AttachmentSource { enum AttachmentSource {
@@ -3,9 +3,7 @@ import 'package:flutter/material.dart';
import 'package:shimmer/shimmer.dart'; import 'package:shimmer/shimmer.dart';
import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart'; import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/visible_footnote.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Widget for showing a GIF attachment /// Widget for showing a GIF attachment
class GiphyAttachment extends AttachmentWidget { class GiphyAttachment extends AttachmentWidget {
@@ -2,11 +2,8 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shimmer/shimmer.dart'; import 'package:shimmer/shimmer.dart';
import 'package:stream_chat_flutter/src/attachment/attachment_title.dart'; import 'package:stream_chat_flutter/src/attachment/attachment_title.dart';
import 'package:stream_chat_flutter/src/attachment/attachment_upload_state_builder.dart';
import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart'; import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Widget for showing an image attachment /// Widget for showing an image attachment
class ImageAttachment extends AttachmentWidget { class ImageAttachment extends AttachmentWidget {
@@ -1,6 +1,5 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/utils.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Widget to display URL attachment /// Widget to display URL attachment
@@ -1,8 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/attachment/attachment_title.dart'; import 'package:stream_chat_flutter/src/attachment/attachment_title.dart';
import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart'; import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart';
import 'package:stream_chat_flutter/src/full_screen_media.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/src/video_thumbnail_image.dart'; import 'package:stream_chat_flutter/src/video_thumbnail_image.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
@@ -1,7 +1,4 @@
import 'dart:ui';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
@@ -1,6 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/unread_indicator.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Back button implementation /// Back button implementation
@@ -2,7 +2,6 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/group_avatar.dart'; import 'package:stream_chat_flutter/src/group_avatar.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_image.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_image.png)
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_image_paint.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_image_paint.png)
@@ -1,13 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:stream_chat_flutter/src/back_button.dart';
import 'package:stream_chat_flutter/src/channel_info.dart'; import 'package:stream_chat_flutter/src/channel_info.dart';
import 'package:stream_chat_flutter/src/channel_name.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/info_tile.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_header.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_header.png)
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_header_paint.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_header_paint.png)
@@ -1,6 +1,5 @@
import 'package:collection/collection.dart' show IterableExtension; import 'package:collection/collection.dart' show IterableExtension;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jiffy/jiffy.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
@@ -1,12 +1,8 @@
import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/stream_neumorphic_button.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Widget builder for title /// Widget builder for title
typedef TitleBuilder = Widget Function( typedef TitleBuilder = Widget Function(
@@ -1,14 +1,10 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:shimmer/shimmer.dart'; import 'package:shimmer/shimmer.dart';
import 'package:stream_chat_flutter/src/channel_bottom_sheet.dart'; import 'package:stream_chat_flutter/src/channel_bottom_sheet.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Callback called when tapping on a channel /// Callback called when tapping on a channel
typedef ChannelTapCallback = void Function(Channel, Widget?); typedef ChannelTapCallback = void Function(Channel, Widget?);
@@ -1,8 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// It shows the current [Channel] name using a [Text] widget. /// It shows the current [Channel] name using a [Text] widget.
/// ///
@@ -1,12 +1,8 @@
import 'package:collection/collection.dart' import 'package:collection/collection.dart'
show IterableExtension, ListEquality; show IterableExtension, ListEquality;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:jiffy/jiffy.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_preview.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_preview.png)
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_preview_paint.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_preview_paint.png)
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Widget that builds itself based on the latest snapshot of interaction with /// Widget that builds itself based on the latest snapshot of interaction with
/// a [Stream] of type [ConnectionStatus]. /// a [Stream] of type [ConnectionStatus].
@@ -1,4 +1,3 @@
import 'package:characters/characters.dart';
import 'package:diacritic/diacritic.dart'; import 'package:diacritic/diacritic.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@@ -6,8 +6,6 @@ import 'package:chewie/chewie.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart'; import 'package:photo_view/photo_view.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/gallery_footer.dart';
import 'package:stream_chat_flutter/src/gallery_header.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:video_player/video_player.dart'; import 'package:video_player/video_player.dart';
@@ -6,11 +6,8 @@ import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/src/video_thumbnail_image.dart'; import 'package:stream_chat_flutter/src/video_thumbnail_image.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Footer widget for media display /// Footer widget for media display
class GalleryFooter extends StatefulWidget implements PreferredSizeWidget { class GalleryFooter extends StatefulWidget implements PreferredSizeWidget {
@@ -1,5 +1,4 @@
import 'dart:math'; import 'dart:math';
import 'dart:ui';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -1,8 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/full_screen_media.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Widget for constructing a group of images in message /// Widget for constructing a group of images in message
class ImageGroup extends StatelessWidget { class ImageGroup extends StatelessWidget {
@@ -5,7 +5,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:photo_manager/photo_manager.dart'; import 'package:photo_manager/photo_manager.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
extension on Duration { extension on Duration {
@@ -1,13 +1,6 @@
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/message_action.dart';
import 'package:stream_chat_flutter/src/reaction_picker.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/src/utils.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Constructs a modal with actions for a message /// Constructs a modal with actions for a message
@@ -4,7 +4,6 @@ import 'dart:math';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
@@ -16,16 +15,12 @@ import 'package:stream_chat_flutter/src/emoji/emoji.dart';
import 'package:stream_chat_flutter/src/emoji_overlay.dart'; import 'package:stream_chat_flutter/src/emoji_overlay.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/media_list_view.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart';
import 'package:stream_chat_flutter/src/message_list_view.dart';
import 'package:stream_chat_flutter/src/multi_overlay.dart'; import 'package:stream_chat_flutter/src/multi_overlay.dart';
import 'package:stream_chat_flutter/src/quoted_message_widget.dart'; import 'package:stream_chat_flutter/src/quoted_message_widget.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/user_mentions_overlay.dart'; import 'package:stream_chat_flutter/src/user_mentions_overlay.dart';
import 'package:stream_chat_flutter/src/video_service.dart'; import 'package:stream_chat_flutter/src/video_service.dart';
import 'package:stream_chat_flutter/src/video_thumbnail_image.dart'; import 'package:stream_chat_flutter/src/video_thumbnail_image.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'package:video_compress/video_compress.dart'; import 'package:video_compress/video_compress.dart';
export 'package:video_compress/video_compress.dart' show VideoQuality; export 'package:video_compress/video_compress.dart' show VideoQuality;
@@ -1129,13 +1124,14 @@ class MessageInputState extends State<MessageInput> {
if (file.size! > widget.maxAttachmentSize) { if (file.size! > widget.maxAttachmentSize) {
if (medium.type == AssetType.video && file.path != null) { if (medium.type == AssetType.video && file.path != null) {
final mediaInfo = await (VideoService.compressVideo( final mediaInfo = await VideoService.compressVideo(
file.path!, file.path!,
frameRate: widget.compressedVideoFrameRate, frameRate: widget.compressedVideoFrameRate,
quality: widget.compressedVideoQuality, quality: widget.compressedVideoQuality,
) as FutureOr<MediaInfo>); );
if (mediaInfo.filesize! > widget.maxAttachmentSize) { if (mediaInfo == null ||
mediaInfo.filesize! > widget.maxAttachmentSize) {
_showErrorAlert( _showErrorAlert(
context.translations.fileTooLargeAfterCompressionError( context.translations.fileTooLargeAfterCompressionError(
widget.maxAttachmentSize / (1024 * 1024), widget.maxAttachmentSize / (1024 * 1024),
@@ -2,20 +2,12 @@
import 'dart:async'; import 'dart:async';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jiffy/jiffy.dart';
import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/info_tile.dart';
import 'package:stream_chat_flutter/src/message_widget.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/swipeable.dart'; import 'package:stream_chat_flutter/src/swipeable.dart';
import 'package:stream_chat_flutter/src/system_message.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'package:visibility_detector/visibility_detector.dart'; import 'package:visibility_detector/visibility_detector.dart';
/// Widget builder for message /// Widget builder for message
@@ -3,12 +3,7 @@ import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/reaction_bubble.dart'; import 'package:stream_chat_flutter/src/reaction_bubble.dart';
import 'package:stream_chat_flutter/src/reaction_picker.dart';
import 'package:stream_chat_flutter/src/stream_chat.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/src/user_avatar.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Modal widget for displaying message reactions /// Modal widget for displaying message reactions
class MessageReactionsModal extends StatelessWidget { class MessageReactionsModal extends StatelessWidget {
@@ -1,8 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jiffy/jiffy.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// It shows the current [Message] preview. /// It shows the current [Message] preview.
/// ///
@@ -1,10 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/info_tile.dart';
import 'package:stream_chat_flutter/src/message_search_item.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Callback called when tapping on a user /// Callback called when tapping on a user
typedef MessageSearchItemTapCallback = void Function(GetMessageResponse); typedef MessageSearchItemTapCallback = void Function(GetMessageResponse);
@@ -1,9 +1,7 @@
import 'package:collection/collection.dart' show IterableExtension; import 'package:collection/collection.dart' show IterableExtension;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Text widget to display in message /// Text widget to display in message
class MessageText extends StatelessWidget { class MessageText extends StatelessWidget {
@@ -1,21 +1,13 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_portal/flutter_portal.dart';
import 'package:jiffy/jiffy.dart';
import 'package:stream_chat_flutter/src/attachment/url_attachment.dart'; import 'package:stream_chat_flutter/src/attachment/url_attachment.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/image_group.dart'; import 'package:stream_chat_flutter/src/image_group.dart';
import 'package:stream_chat_flutter/src/message_action.dart';
import 'package:stream_chat_flutter/src/message_actions_modal.dart'; import 'package:stream_chat_flutter/src/message_actions_modal.dart';
import 'package:stream_chat_flutter/src/message_reactions_modal.dart'; import 'package:stream_chat_flutter/src/message_reactions_modal.dart';
import 'package:stream_chat_flutter/src/quoted_message_widget.dart'; import 'package:stream_chat_flutter/src/quoted_message_widget.dart';
import 'package:stream_chat_flutter/src/reaction_bubble.dart'; import 'package:stream_chat_flutter/src/reaction_bubble.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Widget builder for building attachments /// Widget builder for building attachments
@@ -1,9 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'package:video_player/video_player.dart'; import 'package:video_player/video_player.dart';
/// Widget builder for quoted message attachment thumnail /// Widget builder for quoted message attachment thumnail
@@ -2,9 +2,6 @@ import 'dart:math';
import 'package:collection/collection.dart' show IterableExtension; import 'package:collection/collection.dart' show IterableExtension;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:stream_chat_flutter/src/reaction_icon.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Creates reaction bubble widget for displaying over messages /// Creates reaction bubble widget for displaying over messages
@@ -1,12 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_portal/flutter_portal.dart';
import 'package:jiffy/jiffy.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Widget used to provide information about the chat to the widget tree /// Widget used to provide information about the chat to the widget tree
/// ///
@@ -1,12 +1,5 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart' hide TextTheme; import 'package:flutter/material.dart' hide TextTheme;
import 'package:stream_chat_flutter/src/channel_preview.dart';
import 'package:stream_chat_flutter/src/gradient_avatar.dart';
import 'package:stream_chat_flutter/src/message_input.dart';
import 'package:stream_chat_flutter/src/reaction_icon.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Inherited widget providing the [StreamChatThemeData] to the widget tree /// Inherited widget providing the [StreamChatThemeData] to the widget tree
class StreamChatTheme extends InheritedWidget { class StreamChatTheme extends InheritedWidget {
@@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
@@ -1,9 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/thread_header.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/thread_header.png)
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/thread_header_paint.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/thread_header_paint.png)
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Widget for showing an unread indicator /// Widget for showing an unread indicator
@@ -1,7 +1,6 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Widget that displays a user avatar /// Widget that displays a user avatar
class UserAvatar extends StatelessWidget { class UserAvatar extends StatelessWidget {
@@ -1,10 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jiffy/jiffy.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/user_list_view.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// ///
/// It shows the current [User] preview. /// It shows the current [User] preview.
@@ -1,8 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Callback called when tapping on a user /// Callback called when tapping on a user
typedef UserTapCallback = void Function(User, Widget?); typedef UserTapCallback = void Function(User, Widget?);
@@ -4,7 +4,6 @@ import 'dart:math' as math;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
/// Launch URL /// Launch URL
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Widget for displaying a footnote /// Widget for displaying a footnote
@@ -3,7 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
@@ -3,7 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
@@ -4,11 +4,9 @@
import 'dart:async'; import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:stream_chat_flutter/scrollable_positioned_list/src/scroll_view.dart'; import 'package:stream_chat_flutter/scrollable_positioned_list/src/scroll_view.dart';
@@ -3,7 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
@@ -3,7 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
@@ -3,7 +3,6 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:stream_chat_flutter/src/attachment_actions_modal.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'mocks.dart'; import 'mocks.dart';
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:stream_chat_flutter/src/back_button.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'mocks.dart'; import 'mocks.dart';
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:golden_toolkit/golden_toolkit.dart'; import 'package:golden_toolkit/golden_toolkit.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:stream_chat_flutter/src/gradient_avatar.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'mocks.dart'; import 'mocks.dart';
@@ -1,4 +1,3 @@
import 'dart:async';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@@ -3,6 +3,7 @@
🐞 Fixed 🐞 Fixed
- Do not move a channel to top if the new message is from a thread. - Do not move a channel to top if the new message is from a thread.
- [[#848]](https://github.com/GetStream/stream-chat-flutter/issues/848) Fixed "Bad state: Cannot add new events after calling close" by replacing all `.add` methods with a new `.safeAdd`.
## 3.3.1 ## 3.3.1
@@ -1,11 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/channel_list_core.dart'; import 'package:stream_chat_flutter_core/src/channel_list_core.dart';
import 'package:stream_chat_flutter_core/src/stream_chat_core.dart'; import 'package:stream_chat_flutter_core/src/stream_chat_core.dart';
import 'package:stream_chat_flutter_core/src/stream_controller_extension.dart';
/// Widget dedicated to the management of a channel list with pagination /// Widget dedicated to the management of a channel list with pagination
/// [ChannelsBloc] is used together with [ChannelListCore] to manage a list of /// [ChannelsBloc] is used together with [ChannelListCore] to manage a list of
@@ -116,7 +116,7 @@ class ChannelsBlocState extends State<ChannelsBloc>
} }
if (_channelsController.hasValue) { if (_channelsController.hasValue) {
_queryChannelsLoadingController.add(true); _queryChannelsLoadingController.safeAdd(true);
} }
try { try {
@@ -135,14 +135,14 @@ class ChannelsBlocState extends State<ChannelsBloc>
)) { )) {
newChannels = channels; newChannels = channels;
if (clear) { if (clear) {
_channelsController.add(channels); _channelsController.safeAdd(channels);
} else { } else {
final temp = oldChannels + channels; final temp = oldChannels + channels;
_channelsController.add(temp); _channelsController.safeAdd(temp);
} }
if (_channelsController.hasValue && if (_channelsController.hasValue &&
_queryChannelsLoadingController.value) { _queryChannelsLoadingController.value) {
_queryChannelsLoadingController.sink.add(false); _queryChannelsLoadingController.safeAdd(false);
} }
} }
if (newChannels.isEmpty || newChannels.length < paginationParams.limit) { if (newChannels.isEmpty || newChannels.length < paginationParams.limit) {
@@ -150,11 +150,11 @@ class ChannelsBlocState extends State<ChannelsBloc>
} }
} catch (e, stk) { } catch (e, stk) {
// reset loading controller // reset loading controller
_queryChannelsLoadingController.sink.add(false); _queryChannelsLoadingController.safeAdd(false);
if (_channelsController.hasValue) { if (_channelsController.hasValue) {
_queryChannelsLoadingController.addError(e, stk); _queryChannelsLoadingController.safeAddError(e, stk);
} else { } else {
_channelsController.addError(e, stk); _channelsController.safeAddError(e, stk);
} }
} }
} }
@@ -200,7 +200,7 @@ class ChannelsBlocState extends State<ChannelsBloc>
if (widget.channelsComparator != null) { if (widget.channelsComparator != null) {
newChannels.sort(widget.channelsComparator); newChannels.sort(widget.channelsComparator);
} }
_channelsController.add(newChannels); _channelsController.safeAdd(newChannels);
})); }));
} }
@@ -212,7 +212,7 @@ class ChannelsBlocState extends State<ChannelsBloc>
if (channelIndex > -1) { if (channelIndex > -1) {
final channel = newChannels.removeAt(channelIndex); final channel = newChannels.removeAt(channelIndex);
_hiddenChannels.add(channel); _hiddenChannels.add(channel);
_channelsController.add(newChannels); _channelsController.safeAdd(newChannels);
} }
})) }))
..add(client ..add(client
@@ -222,7 +222,7 @@ class ChannelsBlocState extends State<ChannelsBloc>
) )
.listen((e) { .listen((e) {
final channel = e.channel; final channel = e.channel;
_channelsController.add(List.from( _channelsController.safeAdd(List.from(
(channels ?? [])..removeWhere((c) => c.cid == channel?.cid), (channels ?? [])..removeWhere((c) => c.cid == channel?.cid),
)); ));
})); }));
@@ -2,7 +2,6 @@ import 'dart:async';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/better_stream_builder.dart'; import 'package:stream_chat_flutter_core/src/better_stream_builder.dart';
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/stream_chat_core.dart'; import 'package:stream_chat_flutter_core/src/stream_chat_core.dart';
import 'package:stream_chat_flutter_core/src/stream_controller_extension.dart';
/// [MessageSearchBloc] is used to manage a list of messages with pagination. /// [MessageSearchBloc] is used to manage a list of messages with pagination.
/// This class can be used to load messages, perform queries, etc. /// This class can be used to load messages, perform queries, etc.
@@ -95,7 +96,7 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
} }
if (_messageResponses.hasValue) { if (_messageResponses.hasValue) {
_queryMessagesLoadingController.add(true); _queryMessagesLoadingController.safeAdd(true);
} }
try { try {
final oldMessages = List<GetMessageResponse>.from(messageResponses ?? []); final oldMessages = List<GetMessageResponse>.from(messageResponses ?? []);
@@ -120,24 +121,24 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
final newMessages = response.results; final newMessages = response.results;
if (clear) { if (clear) {
_messageResponses.add(newMessages); _messageResponses.safeAdd(newMessages);
} else { } else {
final temp = oldMessages + newMessages; final temp = oldMessages + newMessages;
_messageResponses.add(temp); _messageResponses.safeAdd(temp);
} }
if (_messageResponses.hasValue && _queryMessagesLoadingController.value) { if (_messageResponses.hasValue && _queryMessagesLoadingController.value) {
_queryMessagesLoadingController.add(false); _queryMessagesLoadingController.safeAdd(false);
} }
if (newMessages.isEmpty || newMessages.length < pagination.limit) { if (newMessages.isEmpty || newMessages.length < pagination.limit) {
_paginationEnded = true; _paginationEnded = true;
} }
} catch (e, stk) { } catch (e, stk) {
// reset loading controller // reset loading controller
_queryMessagesLoadingController.add(false); _queryMessagesLoadingController.safeAdd(false);
if (_messageResponses.hasValue) { if (_messageResponses.hasValue) {
_queryMessagesLoadingController.addError(e, stk); _queryMessagesLoadingController.safeAddError(e, stk);
} else { } else {
_messageResponses.addError(e, stk); _messageResponses.safeAddError(e, stk);
} }
} }
} }
@@ -1,10 +1,10 @@
import 'dart:async'; import 'dart:async';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/stream_controller_extension.dart';
/// Specifies query direction for pagination /// Specifies query direction for pagination
enum QueryDirection { enum QueryDirection {
@@ -92,10 +92,10 @@ class StreamChannelState extends State<StreamChannel> {
channel.state == null) { channel.state == null) {
return; return;
} }
_queryTopMessagesController.add(true); _queryTopMessagesController.safeAdd(true);
if (channel.state!.messages.isEmpty) { if (channel.state!.messages.isEmpty) {
return _queryTopMessagesController.add(false); return _queryTopMessagesController.safeAdd(false);
} }
final oldestMessage = channel.state!.messages.first; final oldestMessage = channel.state!.messages.first;
@@ -109,9 +109,9 @@ class StreamChannelState extends State<StreamChannel> {
if (state.messages.isEmpty || state.messages.length < limit) { if (state.messages.isEmpty || state.messages.length < limit) {
_topPaginationEnded = true; _topPaginationEnded = true;
} }
_queryTopMessagesController.add(false); _queryTopMessagesController.safeAdd(false);
} catch (e, stk) { } catch (e, stk) {
_queryTopMessagesController.addError(e, stk); _queryTopMessagesController.safeAddError(e, stk);
} }
} }
@@ -123,10 +123,10 @@ class StreamChannelState extends State<StreamChannel> {
_queryBottomMessagesController.value || _queryBottomMessagesController.value ||
channel.state == null || channel.state == null ||
channel.state!.isUpToDate) return; channel.state!.isUpToDate) return;
_queryBottomMessagesController.add(true); _queryBottomMessagesController.safeAdd(true);
if (channel.state!.messages.isEmpty) { if (channel.state!.messages.isEmpty) {
return _queryBottomMessagesController.add(false); return _queryBottomMessagesController.safeAdd(false);
} }
final recentMessage = channel.state!.messages.last; final recentMessage = channel.state!.messages.last;
@@ -140,9 +140,9 @@ class StreamChannelState extends State<StreamChannel> {
if (state.messages.isEmpty || state.messages.length < limit) { if (state.messages.isEmpty || state.messages.length < limit) {
_bottomPaginationEnded = true; _bottomPaginationEnded = true;
} }
_queryBottomMessagesController.add(false); _queryBottomMessagesController.safeAdd(false);
} catch (e, stk) { } catch (e, stk) {
_queryBottomMessagesController.addError(e, stk); _queryBottomMessagesController.safeAddError(e, stk);
} }
} }
@@ -166,7 +166,7 @@ class StreamChannelState extends State<StreamChannel> {
if (_topPaginationEnded || if (_topPaginationEnded ||
_queryTopMessagesController.value || _queryTopMessagesController.value ||
channel.state == null) return; channel.state == null) return;
_queryTopMessagesController.add(true); _queryTopMessagesController.safeAdd(true);
Message? message; Message? message;
if (channel.state!.threads.containsKey(parentId)) { if (channel.state!.threads.containsKey(parentId)) {
@@ -188,9 +188,9 @@ class StreamChannelState extends State<StreamChannel> {
if (response.messages.isEmpty || response.messages.length < limit) { if (response.messages.isEmpty || response.messages.length < limit) {
_topPaginationEnded = true; _topPaginationEnded = true;
} }
_queryTopMessagesController.add(false); _queryTopMessagesController.safeAdd(false);
} catch (e, stk) { } catch (e, stk) {
_queryTopMessagesController.addError(e, stk); _queryTopMessagesController.safeAddError(e, stk);
} }
} }
@@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/typedef.dart'; import 'package:stream_chat_flutter_core/src/typedef.dart';
@@ -0,0 +1,18 @@
import 'dart:async';
/// Extension on [StreamController] to safely add events and errors.
extension StreamControllerX<T> on StreamController<T> {
/// Safely adds the event to the controller,
/// Returns early if the controller is closed.
void safeAdd(T event) {
if (isClosed) return;
add(event);
}
/// Safely adds the error to the controller,
/// Returns early if the controller is closed.
void safeAddError(Object error, [StackTrace? stackTrace]) {
if (isClosed) return;
addError(error, stackTrace);
}
}
@@ -2,8 +2,6 @@ import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/users_bloc.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// ///
@@ -1,7 +1,6 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter_core/src/stream_controller_extension.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Widget dedicated to the management of a users list with pagination. /// Widget dedicated to the management of a users list with pagination.
@@ -82,7 +81,7 @@ class UsersBlocState extends State<UsersBloc>
} }
if (_usersController.hasValue) { if (_usersController.hasValue) {
_queryUsersLoadingController.add(true); _queryUsersLoadingController.safeAdd(true);
} }
try { try {
@@ -97,24 +96,24 @@ class UsersBlocState extends State<UsersBloc>
final newUsers = usersResponse.users; final newUsers = usersResponse.users;
if (clear) { if (clear) {
_usersController.add(usersResponse.users); _usersController.safeAdd(usersResponse.users);
} else { } else {
final temp = oldUsers + usersResponse.users; final temp = oldUsers + usersResponse.users;
_usersController.add(temp); _usersController.safeAdd(temp);
} }
if (_usersController.hasValue && _queryUsersLoadingController.value) { if (_usersController.hasValue && _queryUsersLoadingController.value) {
_queryUsersLoadingController.add(false); _queryUsersLoadingController.safeAdd(false);
} }
if (newUsers.isEmpty || newUsers.length < pagination.limit) { if (newUsers.isEmpty || newUsers.length < pagination.limit) {
_paginationEnded = true; _paginationEnded = true;
} }
} catch (e, stk) { } catch (e, stk) {
// reset loading controller // reset loading controller
_queryUsersLoadingController.add(false); _queryUsersLoadingController.safeAdd(false);
if (_usersController.hasValue) { if (_usersController.hasValue) {
_queryUsersLoadingController.addError(e, stk); _queryUsersLoadingController.safeAddError(e, stk);
} else { } else {
_usersController.addError(e, stk); _usersController.safeAddError(e, stk);
} }
} }
} }
@@ -1,8 +1,6 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/message_search_bloc.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'matchers/get_message_response_matcher.dart'; import 'matchers/get_message_response_matcher.dart';
@@ -1,7 +1,4 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
@@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
@@ -1,3 +1,9 @@
## 2.1.0
🔄 Changed
* Some of the `Japanese` translations have been updated/changed for better understanding.
## 2.0.0 ## 2.0.0
- Updated `stream_chat_flutter` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - Updated `stream_chat_flutter` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat_flutter/changelog).
@@ -340,10 +340,10 @@ class StreamChatLocalizationsHi extends GlobalStreamChatLocalizations {
String get sendLabel => 'भेजें'; String get sendLabel => 'भेजें';
@override @override
String get withText => 'विद'; //TODO: break? String get withText => 'विद';
@override @override
String get inText => 'इन'; //TODO: break? String get inText => 'इन';
@override @override
String get youText => 'आप'; String get youText => 'आप';
@@ -38,7 +38,7 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
String get threadReplyLabel => 'スレッド返信'; String get threadReplyLabel => 'スレッド返信';
@override @override
String get onlyVisibleToYouText => '自分しか見れません'; String get onlyVisibleToYouText => '自分にのみ見えます';
@override @override
String threadReplyCountText(int count) => '$countつのスレッド返信'; String threadReplyCountText(int count) => '$countつのスレッド返信';
@@ -48,7 +48,7 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
required int remaining, required int remaining,
required int total, required int total,
}) => }) =>
'$remaining/${total}mbのアップロード中 。。。'; '$remaining/${total}mbのアップロード中';
@override @override
String pinnedByUserText({ String pinnedByUserText({
@@ -82,7 +82,7 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
String get messageReactionsLabel => 'メッセージのリアクション'; String get messageReactionsLabel => 'メッセージのリアクション';
@override @override
String get emptyChatMessagesText => 'チャットがありませんが。。。'; String get emptyChatMessagesText => 'まだメッセージはありません';
@override @override
String threadSeparatorText(int replyCount) => '$replyCount件の返信'; String threadSeparatorText(int replyCount) => '$replyCount件の返信';
@@ -94,7 +94,7 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
String get disconnectedLabel => '接続切れ'; String get disconnectedLabel => '接続切れ';
@override @override
String get reconnectingLabel => '再接続中。。。'; String get reconnectingLabel => '再接続中';
@override @override
String get alsoSendAsDirectMessageLabel => 'ダイレクトメッセージでも送信'; String get alsoSendAsDirectMessageLabel => 'ダイレクトメッセージでも送信';
@@ -115,7 +115,7 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
String fileTooLargeAfterCompressionError(double limitInMB) => String fileTooLargeAfterCompressionError(double limitInMB) =>
'ファイルのサイズが大きすぎてアップロードできません。' 'ファイルのサイズが大きすぎてアップロードできません。'
'ファイルサイズの制限は${limitInMB}MBです。' 'ファイルサイズの制限は${limitInMB}MBです。'
'圧縮してみましたが、十分ではありませんでした'; '圧縮を試しましたがサイズをオーバーしました';
@override @override
String fileTooLargeError(double limitInMB) => String fileTooLargeError(double limitInMB) =>
@@ -143,7 +143,7 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
String get videoFromCameraLabel => 'カメラからの動画'; String get videoFromCameraLabel => 'カメラからの動画';
@override @override
String get okLabel => 'よし'; String get okLabel => 'OK';
@override @override
String get somethingWentWrongError => 'エラーが発生しました'; String get somethingWentWrongError => 'エラーが発生しました';
@@ -180,7 +180,7 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
String get deleteLabel => '削除'; String get deleteLabel => '削除';
@override @override
String get deleteMessageLabel => 'メッセージを削除する '; String get deleteMessageLabel => 'メッセージを削除する';
@override @override
String get deleteMessageQuestion => 'このメッセージ' String get deleteMessageQuestion => 'このメッセージ'
@@ -195,7 +195,7 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
@override @override
String togglePinUnpinText({required bool pinned}) { String togglePinUnpinText({required bool pinned}) {
if (pinned) return '会話のピンを外す'; if (pinned) return '会話のピンを外す';
return '会話ピンする'; return '会話ピンする';
} }
@override @override
@@ -246,7 +246,7 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
String get yesterdayLabel => '昨日'; String get yesterdayLabel => '昨日';
@override @override
String get channelIsMutedText => 'チャンネルが無音されています'; String get channelIsMutedText => 'チャンネルがミュートされています';
@override @override
String get noTitleText => 'タイトル無し'; String get noTitleText => 'タイトル無し';
@@ -255,7 +255,7 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
String get letsStartChattingLabel => 'チャットを始めよう!'; String get letsStartChattingLabel => 'チャットを始めよう!';
@override @override
String get sendingFirstMessageLabel => '友人に最初のメッセージを送ましょうか?'; String get sendingFirstMessageLabel => '最初のメッセージを送ってみましょう';
@override @override
String get startAChatLabel => 'チャットを開始する'; String get startAChatLabel => 'チャットを開始する';
@@ -276,7 +276,7 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
String get searchingForNetworkText => 'ネットワークを検索中'; String get searchingForNetworkText => 'ネットワークを検索中';
@override @override
String get offlineLabel => 'オフライン。。。'; String get offlineLabel => 'オフライン';
@override @override
String get tryAgainLabel => '再試行する'; String get tryAgainLabel => '再試行する';
@@ -291,16 +291,16 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
String get viewInfoLabel => '情報を見る'; String get viewInfoLabel => '情報を見る';
@override @override
String get leaveGroupLabel => 'グループを離れ'; String get leaveGroupLabel => 'グループから退出す';
@override @override
String get leaveLabel => '離れ'; String get leaveLabel => '退出す';
@override @override
String get leaveConversationLabel => '会話を離れ'; String get leaveConversationLabel => '会話から退出す';
@override @override
String get leaveConversationQuestion => '本当に会話を離れますか?'; String get leaveConversationQuestion => '本当に会話から退出しますか?';
@override @override
String get showInChatLabel => 'チャットで表示'; String get showInChatLabel => 'チャットで表示';
@@ -315,7 +315,7 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
String get uploadErrorLabel => 'アップロードエラー'; String get uploadErrorLabel => 'アップロードエラー';
@override @override
String get giphyLabel => 'ギフィー'; String get giphyLabel => 'GIPHY';
@override @override
String get shuffleLabel => 'ミックス'; String get shuffleLabel => 'ミックス';
@@ -1,3 +1,7 @@
## Upcoming
- Bump `drift` to `1.3.0`.
## 3.0.0 ## 3.0.0
- Updated `stream_chat` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat/changelog). - Updated `stream_chat` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat/changelog).

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