Merge branch 'develop' into v4

This commit is contained in:
Salvatore Giordano
2022-02-01 16:34:59 +01:00
10 changed files with 456 additions and 41 deletions
+4 -5
View File
@@ -5,6 +5,9 @@
- [[#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.
- [[#871]](https://github.com/GetStream/stream-chat-flutter/issues/871) Fixed thread message deletion.
- [[#846]](https://github.com/GetStream/stream-chat-flutter/issues/846) Fixed `message.ownReactions` getting truncated when receiving a reaction event.
- Add check for invalid image URLs
🔄 Changed
@@ -19,10 +22,6 @@
- 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.
## 3.3.1
🐞 Fixed
@@ -767,4 +766,4 @@
## 0.0.2
- first beta version
- first beta version
@@ -841,7 +841,7 @@ class Channel {
final now = DateTime.now();
final user = _client.state.currentUser;
final latestReactions = [...message.latestReactions ?? <Reaction>[]];
var latestReactions = [...message.latestReactions ?? <Reaction>[]];
if (enforceUnique) {
latestReactions.removeWhere((it) => it.userId == user!.id);
}
@@ -855,10 +855,17 @@ class Channel {
extraData: extraData,
);
// Inserting at the 0th index as it's the latest reaction
latestReactions.insert(0, newReaction);
final ownReactions = [...latestReactions]
..removeWhere((it) => it.userId != user!.id);
latestReactions = (latestReactions
// Inserting at the 0th index as it's the latest reaction
..insert(0, newReaction))
.take(10)
.toList();
final ownReactions = enforceUnique
? <Reaction>[newReaction]
: <Reaction>[
...message.ownReactions ?? [],
newReaction,
];
final newMessage = message.copyWith(
reactionCounts: {...message.reactionCounts ?? <String, int>{}}
@@ -898,7 +905,6 @@ class Channel {
Reaction reaction,
) async {
final type = reaction.type;
final user = _client.state.currentUser;
final reactionCounts = {...message.reactionCounts ?? <String, int>{}};
if (reactionCounts.containsKey(type)) {
@@ -915,8 +921,11 @@ class Channel {
r.type == reaction.type &&
r.messageId == reaction.messageId);
final ownReactions = [...latestReactions]
..removeWhere((it) => it.userId != user!.id);
final ownReactions = message.ownReactions
?..removeWhere((r) =>
r.userId == reaction.userId &&
r.type == reaction.type &&
r.messageId == reaction.messageId);
final newMessage = message.copyWith(
reactionCounts: reactionCounts..removeWhere((_, value) => value == 0),
@@ -1545,16 +1554,21 @@ class ChannelClientState {
if (url == null || !url.contains('')) {
return false;
}
final uri = Uri.parse(url);
if (!uri.host.endsWith('stream-io-cdn.com') ||
uri.queryParameters['Expires'] == null) {
try {
final uri = Uri.parse(url);
if (!uri.host.endsWith('stream-io-cdn.com') ||
uri.queryParameters['Expires'] == null) {
return false;
}
final secondsFromEpoch =
int.parse(uri.queryParameters['Expires']!);
final expiration = DateTime.fromMillisecondsSinceEpoch(
secondsFromEpoch * 1000,
);
return expiration.isBefore(DateTime.now());
} catch (_) {
return false;
}
final secondsFromEpoch =
int.parse(uri.queryParameters['Expires']!);
final expiration =
DateTime.fromMillisecondsSinceEpoch(secondsFromEpoch * 1000);
return expiration.isBefore(DateTime.now());
}))
.map((e) => e.id)
.toList();
@@ -1702,10 +1716,19 @@ class ChannelClientState {
void _listenReactionDeleted() {
_subscriptions.add(_channel.on(EventType.reactionDeleted).listen((event) {
final userId = _channel.client.state.currentUser!.id;
final oldMessage =
messages.firstWhereOrNull((it) => it.id == event.message?.id);
final reaction = event.reaction;
final ownReactions = oldMessage?.ownReactions
?.whereNot((it) =>
it.type == reaction?.type &&
it.score == reaction?.score &&
it.messageId == reaction?.messageId &&
it.userId == reaction?.userId &&
it.extraData == reaction?.extraData)
.toList(growable: false);
final message = event.message!.copyWith(
ownReactions: [...event.message!.latestReactions!]
..removeWhere((it) => it.userId != userId),
ownReactions: ownReactions,
);
addMessage(message);
}));
@@ -1713,10 +1736,10 @@ class ChannelClientState {
void _listenReactions() {
_subscriptions.add(_channel.on(EventType.reactionNew).listen((event) {
final userId = _channel.client.state.currentUser!.id;
final oldMessage =
messages.firstWhereOrNull((it) => it.id == event.message?.id);
final message = event.message!.copyWith(
ownReactions: [...event.message!.latestReactions!]
..removeWhere((it) => it.userId != userId),
ownReactions: oldMessage?.ownReactions,
);
addMessage(message);
}));
@@ -1729,10 +1752,11 @@ class ChannelClientState {
EventType.reactionUpdated,
)
.listen((event) {
final userId = _channel.client.state.currentUser!.id;
final oldMessage =
messages.firstWhereOrNull((it) => it.id == event.message?.id);
final message = event.message!.copyWith(
ownReactions: [...event.message!.latestReactions!]
..removeWhere((it) => it.userId != userId),
ownReactions: oldMessage?.ownReactions,
);
addMessage(message);
+1 -1
View File
@@ -23,7 +23,7 @@ dependencies:
flutter_markdown: ^0.6.1
flutter_portal: ^0.4.0
flutter_slidable: ^0.6.0
flutter_svg: ^0.23.0+1
flutter_svg: ^1.0.1
http_parser: ^4.0.0
image_gallery_saver: ^1.7.0
image_picker: ^0.8.2
@@ -1,4 +1,8 @@
## 2.1.0
## Upcoming
✅ Added
* Added support for [Portuguese](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart) locale.
🔄 Changed
+8 -5
View File
@@ -37,6 +37,7 @@ At the moment we support the following languages:
- [Spanish](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart)
- [Japanese](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart)
- [Korean](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart)
- [Portuguese](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart)
More languages will be added in the future. Feel free to [contribute](https://github.com/GetStream/stream-chat-flutter/blob/master/CONTRIBUTING.md) to add more languages.
@@ -74,6 +75,7 @@ class MyApp extends StatelessWidget {
Locale('es'),
Locale('ja'),
Locale('ko'),
Locale('pt'),
],
// Add GlobalStreamChatLocalizations.delegates
localizationsDelegates: GlobalStreamChatLocalizations.delegates,
@@ -112,13 +114,14 @@ Example:
```xml
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>nb</string>
<string>fr</string>
<string>it</string>
<string>es</string>
<string>en</string>
<string>hi</string>
<string>fr</string>
<string>it</string>
<string>es</string>
<string>ja</string>
<string>ko</string>
<string>pt</string>
</array>
```
@@ -4,10 +4,14 @@
<dict>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>it</string>
<string>fr</string>
<string>hi</string>
<string>en</string>
<string>hi</string>
<string>fr</string>
<string>it</string>
<string>es</string>
<string>ja</string>
<string>ko</string>
<string>pt</string>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
@@ -75,6 +75,7 @@ class MyApp extends StatelessWidget {
Locale('es'),
Locale('ja'),
Locale('ko'),
Locale('pt'),
],
// Add GlobalStreamChatLocalizations.delegates
localizationsDelegates: GlobalStreamChatLocalizations.delegates,
@@ -100,6 +100,7 @@ class MyApp extends StatelessWidget {
Locale('es'),
Locale('ja'),
Locale('ko'),
Locale('pt'),
],
// Add overridden "CustomStreamChatLocalizationsEn.delegate" along with
// "GlobalStreamChatLocalizations.delegates"
@@ -17,6 +17,8 @@ part 'stream_chat_localizations_ko.dart';
part 'stream_chat_localizations_hi.dart';
part 'stream_chat_localizations_pt.dart';
/// The set of supported languages, as language code strings.
///
/// The [GlobalStreamChatLocalizations.delegate] can generate localizations for
@@ -33,6 +35,7 @@ const kStreamChatSupportedLanguages = {
'es',
'ja',
'ko',
'pt',
};
/// Creates a [GlobalStreamChatLocalizations] instance for the given `locale`.
@@ -69,6 +72,8 @@ GlobalStreamChatLocalizations? getStreamChatTranslation(Locale locale) {
return const StreamChatLocalizationsJa();
case 'ko':
return const StreamChatLocalizationsKo();
case 'pt':
return const StreamChatLocalizationsPt();
}
}
@@ -0,0 +1,374 @@
part of 'stream_chat_localizations.dart';
/// The translations for Portuguese (`pt`).
class StreamChatLocalizationsPt extends GlobalStreamChatLocalizations {
/// Create an instance of the translation bundle for Portuguese.
const StreamChatLocalizationsPt({String localeName = 'pt'})
: super(localeName: localeName);
@override
String get launchUrlError => 'O URL não pôde ser aberto';
@override
String get loadingUsersError => 'Erro de carregamento do usuário';
@override
String get noUsersLabel => 'Nenhum usuário atualmente';
@override
String get retryLabel => 'Tente novamente';
@override
String get userLastOnlineText => 'Última vez on-line';
@override
String get userOnlineText => 'Online';
@override
String userTypingText(Iterable<User> users) {
if (users.isEmpty) return '';
final first = users.first;
if (users.length == 1) {
return '${first.name} está digitando';
}
return '${first.name} e ${users.length - 1} estão digitando';
}
@override
String get threadReplyLabel => 'Responder na conversa';
@override
String get onlyVisibleToYouText => 'Visível apenas para você';
@override
String threadReplyCountText(int count) => '$count respostas na conversa';
@override
String attachmentsUploadProgressText({
required int remaining,
required int total,
}) =>
'Tranferência em andamento $remaining/$total ...';
@override
String pinnedByUserText({
required User pinnedBy,
required User currentUser,
}) {
final pinnedByCurrentUser = currentUser.id == pinnedBy.id;
if (pinnedByCurrentUser) return 'Definido por você';
return 'Definido por ${pinnedBy.name}';
}
@override
String get emptyMessagesText => 'Não há mensagens';
@override
String get genericErrorText => 'Ocorreu um problema';
@override
String get loadingMessagesError => 'Ocorreu um problema ao carregar mensagem';
@override
String resultCountText(int count) => '$count resultados';
@override
String get messageDeletedText => 'Esta mensagem foi excluída.';
@override
String get messageDeletedLabel => 'Mensagem excluída';
@override
String get messageReactionsLabel => 'Reações às mensagens';
@override
String get emptyChatMessagesText => 'Ainda não há mensagens aqui...';
@override
String threadSeparatorText(int replyCount) {
if (replyCount == 1) return '1 resposta';
return '$replyCount respostas';
}
@override
String get connectedLabel => 'Conectado';
@override
String get disconnectedLabel => 'Desconectado';
@override
String get reconnectingLabel => 'Reconectando...';
@override
String get alsoSendAsDirectMessageLabel =>
'Enviar também como mensagem direta';
@override
String get addACommentOrSendLabel => 'Adicionar um comnetário ou enviar';
@override
String get searchGifLabel => 'Pesquisar GIFs';
@override
String get writeAMessageLabel => 'Escrever uma mensagem';
@override
String get instantCommandsLabel => 'Comandos instantâneos';
@override
String fileTooLargeAfterCompressionError(double limitInMB) =>
'O arquivo é muito grande para carregamento. '
'O tamanho máximo do arquivo é de $limitInMB MB. '
'Tentamos comprimi-lo, mas não foi suficiente.';
@override
String fileTooLargeError(double limitInMB) =>
'O arquivo é muito grande para carregamento. '
'O tamanho máximo dos arquivos é de $limitInMB MB.';
@override
String emojiMatchingQueryText(String query) =>
'Emoji correspondente a "$query"';
@override
String get addAFileLabel => 'Adicionar um arquivo';
@override
String get photoFromCameraLabel => 'Foto da câmera';
@override
String get uploadAFileLabel => 'Transferir um arquivo';
@override
String get uploadAPhotoLabel => 'Carregar uma foto';
@override
String get uploadAVideoLabel => 'Carregar um vídeo';
@override
String get videoFromCameraLabel => 'Vídeo da câmera';
@override
String get okLabel => 'OK';
@override
String get somethingWentWrongError => 'Algo deu errado';
@override
String get addMoreFilesLabel => 'Adicionar mais arquivos';
@override
String get enablePhotoAndVideoAccessMessage =>
'Por favor, permita o acesso a suas fotos'
'\ne vídeos para que possa compartilhar com sua rede.';
@override
String get allowGalleryAccessMessage => 'Permitir acesso à sua galeria';
@override
String get flagMessageLabel => 'Denunciar mensagem';
@override
String get flagMessageQuestion => 'Gostaria de enviar esta mensagem ao'
'\nmoderador para maior investigação?';
@override
String get flagLabel => 'DENUNCIAR';
@override
String get cancelLabel => 'CANCELAR';
@override
String get flagMessageSuccessfulLabel => 'Mensagem denunciada';
@override
String get flagMessageSuccessfulText =>
'Esta mensagem foi enviada a um moderador.';
@override
String get deleteLabel => 'APAGAR';
@override
String get deleteMessageLabel => 'Apagar mensagem';
@override
String get deleteMessageQuestion =>
'Você tem certeza que deseja apagar essa\nmensagem permanentemente?';
@override
String get operationCouldNotBeCompletedText =>
'A operação não pode ser completada.';
@override
String get replyLabel => 'Resposta';
@override
String togglePinUnpinText({required bool pinned}) {
if (pinned) return 'Desafixar na conversa';
return 'Fixar na conversa';
}
@override
String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) {
if (isDeleteFailed) return 'Repetir apagar mensagem';
return 'Apagar mensagem';
}
@override
String get copyMessageLabel => 'Copiar mensagem';
@override
String get editMessageLabel => 'Editar mensagem';
@override
String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) {
if (isUpdateFailed) return 'Reenviar mensagem alterada';
return 'Reenviar';
}
@override
String get photosLabel => 'Fotos';
String _getDay(DateTime dateTime) {
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
final yesterday = DateTime(now.year, now.month, now.day - 1);
final date = DateTime(dateTime.year, dateTime.month, dateTime.day);
if (date == today) {
return 'Hoje';
} else if (date == yesterday) {
return 'Ontem';
} else {
return 'o ${Jiffy(date).MMMd}';
}
}
@override
String sentAtText({required DateTime date, required DateTime time}) =>
'''Enviado ${_getDay(date)} às ${Jiffy(time.toLocal()).format('HH:mm')}''';
@override
String get todayLabel => 'Hoje';
@override
String get yesterdayLabel => 'Ontem';
@override
String get channelIsMutedText => 'O canal está silenciado';
@override
String get noTitleText => 'Sem título';
@override
String get letsStartChattingLabel => 'Vamos começar a conversar!';
@override
String get sendingFirstMessageLabel =>
'Que tal enviar sua primeira mensagem a um amigo?';
@override
String get startAChatLabel => 'Iniciar uma conversa';
@override
String get loadingChannelsError => 'Erro ao carregar os canais';
@override
String get deleteConversationLabel => 'Apagar a conversa';
@override
String get deleteConversationQuestion =>
'Tem certeza que deseja apagar essa conversa?';
@override
String get streamChatLabel => 'Stream Chat';
@override
String get searchingForNetworkText => 'Pesquisando rede';
@override
String get offlineLabel => 'Sem conexão...';
@override
String get tryAgainLabel => 'Tente novamente';
@override
String membersCountText(int count) {
if (count == 1) return '1 membro';
return '$count membros';
}
@override
String watchersCountText(int count) {
if (count == 1) return '1 online';
return '$count online';
}
@override
String get viewInfoLabel => 'Ver informação';
@override
String get leaveGroupLabel => 'Sair do grupo';
@override
String get leaveLabel => 'SAIR';
@override
String get leaveConversationLabel => 'Sair da conversa';
@override
String get leaveConversationQuestion =>
'Tem certeza que deseja sair dessa conversa?';
@override
String get showInChatLabel => 'Mostrar no chat';
@override
String get saveImageLabel => 'Salvar imagem';
@override
String get saveVideoLabel => 'Salvar vídeo';
@override
String get uploadErrorLabel => 'ERRO DE TRANSFERÊNCIA';
@override
String get giphyLabel => 'Giphy';
@override
String get shuffleLabel => 'Misturar';
@override
String get sendLabel => 'Enviar';
@override
String get withText => 'com';
@override
String get inText => 'em';
@override
String get youText => 'Você';
@override
String galleryPaginationText({
required int currentPage,
required int totalPages,
}) =>
'${currentPage + 1} de $totalPages';
@override
String get fileText => 'Arquivo';
@override
String get replyToMessageLabel => 'Responder à mensagem';
@override
String attachmentLimitExceedError(int limit) => '''
Não é possível adicionar mais de $limit arquivos de uma vez
''';
@override
String get slowModeOnLabel => 'Modo lento ativado';
}