Merge branch 'feature/new-ui' of https://github.com/GetStream/stream-chat-flutter into feature/flag

 Conflicts:
	lib/src/message_actions_modal.dart
	lib/src/message_list_view.dart
	lib/src/message_widget.dart
This commit is contained in:
Deven Joshi
2021-01-11 21:38:12 +05:30
18 changed files with 458 additions and 320 deletions
+7 -1
View File
@@ -9,11 +9,15 @@ class StreamBackButton extends StatelessWidget {
Key key,
this.onPressed,
this.showUnreads = false,
this.cid,
}) : super(key: key);
final VoidCallback onPressed;
final bool showUnreads;
/// Channel cid used to retrieve unread count
final String cid;
@override
Widget build(BuildContext context) {
return Stack(
@@ -45,7 +49,9 @@ class StreamBackButton extends StatelessWidget {
Positioned(
top: 7,
right: 7,
child: UnreadIndicator(),
child: UnreadIndicator(
cid: cid,
),
),
],
);
+1 -4
View File
@@ -63,10 +63,7 @@ class DeletedMessage extends StatelessWidget {
'Message deleted',
style: messageTheme.messageText.copyWith(
fontStyle: FontStyle.italic,
color: (Theme.of(context).brightness == Brightness.dark
? StreamChatTheme.of(context).colorTheme.white
: StreamChatTheme.of(context).colorTheme.black)
.withOpacity(.5),
color: messageTheme.createdAt.color,
),
),
),
+1 -1
View File
@@ -116,7 +116,7 @@ class _ImageFooterState extends State<ImageFooter> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: StreamSvgIcon.icon_SHARE(
icon: StreamSvgIcon.Icon_SHARE(
size: 24.0,
color: StreamChatTheme.of(context).colorTheme.black,
),
+8 -8
View File
@@ -32,8 +32,8 @@ class MessageActionsModal extends StatefulWidget {
final bool showCopyMessage;
final bool showEditMessage;
final bool showResendMessage;
final bool showReply;
final bool showThreadReply;
final bool showReplyMessage;
final bool showThreadReplyMessage;
final bool showFlagButton;
final bool reverse;
final ShapeBorder messageShape;
@@ -49,9 +49,9 @@ class MessageActionsModal extends StatefulWidget {
this.onReplyTap,
this.onThreadReplyTap,
this.showCopyMessage = true,
this.showReply = true,
this.showReplyMessage = true,
this.showResendMessage = true,
this.showThreadReply = true,
this.showThreadReplyMessage = true,
this.showFlagButton = true,
this.showUserAvatar = DisplayWidget.show,
this.editMessageInputBuilder,
@@ -148,8 +148,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
showReactions: false,
showUsername: false,
showThreadReplyIndicator: false,
showReplyIndicator: false,
showUserAvatar: widget.showUserAvatar,
showReplyMessage: false,
showUserAvatar: showUserAvatar,
showTimestamp: false,
translateUserAvatar: false,
showReactionPickerIndicator:
@@ -200,14 +200,14 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
.greyWhisper,
context: context,
tiles: [
if (widget.showReply &&
if (widget.showReplyMessage &&
(widget.message.status ==
MessageSendingStatus.SENT ||
widget.message.status ==
null) &&
widget.message.parentId == null)
_buildReplyButton(context),
if (widget.showThreadReply &&
if (widget.showThreadReplyMessage &&
(widget.message.status ==
MessageSendingStatus.SENT ||
widget.message.status ==
+62 -41
View File
@@ -187,7 +187,6 @@ class MessageListView extends StatefulWidget {
class _MessageListViewState extends State<MessageListView> {
ItemScrollController _scrollController;
bool _bottomWasVisible = false;
Function _onThreadTap;
bool _showScrollToBottom = false;
ItemPositionsListener _itemPositionListener;
@@ -220,6 +219,8 @@ class _MessageListViewState extends State<MessageListView> {
bool get _upToDate => streamChannel.channel.state.isUpToDate;
bool get _isThreadConversation => widget.parentMessage != null;
bool _topPaginationActive = false;
bool _bottomPaginationActive = false;
@@ -234,7 +235,7 @@ class _MessageListViewState extends State<MessageListView> {
@override
Widget build(BuildContext context) {
final messagesStream = widget.parentMessage != null
final messagesStream = _isThreadConversation
? streamChannel.channel.state.threadsStream
.where((threads) => threads.containsKey(widget.parentMessage.id))
.map((threads) => threads[widget.parentMessage.id])
@@ -333,9 +334,8 @@ class _MessageListViewState extends State<MessageListView> {
physics: widget.scrollPhysics,
itemScrollController: _scrollController,
reverse: true,
itemCount: messages.length +
2 +
(widget.parentMessage != null ? 1 : 0),
itemCount:
messages.length + 2 + (_isThreadConversation ? 1 : 0),
separatorBuilder: (context, i) {
if (i == messages.length) return Offstage();
if (i == messages.length + 2) return Offstage();
@@ -368,7 +368,11 @@ class _MessageListViewState extends State<MessageListView> {
final isNextUserSame =
message.user.id == nextMessage.user?.id;
final isThread = message.replyCount > 0;
if (timeDiff >= 1 || !isNextUserSame || isThread) {
final isDeleted = message.isDeleted;
if (timeDiff >= 1 ||
!isNextUserSame ||
isThread ||
isDeleted) {
return SizedBox(height: 8);
}
return SizedBox(height: 2);
@@ -497,7 +501,7 @@ class _MessageListViewState extends State<MessageListView> {
Future<void> _paginateData(
StreamChannelState channel, QueryDirection direction) {
if (widget.parentMessage == null) {
if (!_isThreadConversation) {
return channel.queryMessages(direction: direction);
} else {
return channel.getReplies(widget.parentMessage.id);
@@ -687,14 +691,13 @@ class _MessageListViewState extends State<MessageListView> {
key: ValueKey<String>('BOTTOM-MESSAGE'),
onVisibilityChanged: (visibility) {
final isVisible = visibility.visibleBounds != Rect.zero;
if (isVisible && !_bottomWasVisible) {
if (isVisible) {
final channel = streamChannel.channel;
if (_upToDate &&
channel.config?.readEvents == true &&
channel.state.unreadCount > 0) {
streamChannel.channel.markRead();
}
_bottomWasVisible = !isVisible;
}
if (mounted) {
setState(() => _showScrollToBottom = !isVisible);
@@ -712,7 +715,12 @@ class _MessageListViewState extends State<MessageListView> {
return MessageWidget(
showThreadReplyIndicator: false,
showInChannelIndicator: false,
showReplyIndicator: false,
showReplyMessage: false,
showResendMessage: false,
showThreadReplyMessage: false,
showCopyMessage: false,
showDeleteMessage: false,
showEditMessage: false,
message: message,
reverse: isMyMessage,
showUsername: !isMyMessage,
@@ -722,10 +730,8 @@ class _MessageListViewState extends State<MessageListView> {
right: 8.0,
bottom: 16.0,
),
showSendingIndicator: DisplayWidget.hide,
showSendingIndicator: false,
onThreadTap: _onThreadTap,
showEditMessage: false,
showDeleteMessage: false,
borderRadiusGeometry: BorderRadius.only(
topLeft: Radius.circular(16),
bottomLeft: Radius.circular(2),
@@ -768,32 +774,60 @@ class _MessageListViewState extends State<MessageListView> {
}
final channel = streamChannel.channel;
final readList = channel.state?.read
?.where((element) => element.user.id != userId)
?.where((read) =>
(read.lastRead.isAfter(message.createdAt) ||
read.lastRead.isAtSameMomentAs(message.createdAt)) &&
(index == 0 ||
read.lastRead.isBefore(messages[index - 1].createdAt)))
?.toList() ??
final readList = channel.state?.read?.where((read) {
if (read.user.id == userId) return false;
return (read.lastRead.isAfter(message.createdAt) ||
read.lastRead.isAtSameMomentAs(message.createdAt)) &&
(index == 0 ||
read.lastRead.isBefore(messages[index - 1].createdAt));
})?.toList() ??
[];
final allRead = readList.length >= (channel.memberCount ?? 0) - 1;
final isThreadMessage =
widget.parentMessage != null || message?.showInChannel == true;
final hasFileAttachment =
message.attachments.any((it) => it.type == 'file');
final isThreadMessage =
message?.parentId != null && message?.showInChannel == true;
final hasReplies = message.replyCount > 0;
final attachmentBorderRadius = hasFileAttachment ? 12.0 : 14.0;
final showTimeStamp = message.createdAt != null &&
(!isThreadMessage || _isThreadConversation) &&
!hasReplies &&
(timeDiff >= 1 || !isNextUserSame);
final showUsername = !isMyMessage &&
(!isThreadMessage || _isThreadConversation) &&
!hasReplies &&
(timeDiff >= 1 || !isNextUserSame);
final showUserAvatar = isMyMessage
? DisplayWidget.gone
: (timeDiff >= 1 || !isNextUserSame)
? DisplayWidget.show
: DisplayWidget.hide;
final showSendingIndicator =
isMyMessage && (index == 0 || timeDiff >= 1 || !isNextUserSame);
bool showInChannelIndicator = !_isThreadConversation && isThreadMessage;
bool showThreadReplyIndicator = !_isThreadConversation && hasReplies;
Widget child = MessageWidget(
key: ValueKey<String>('MESSAGE-${message.id}'),
message: message,
reverse: isMyMessage,
showReactions: !message.isDeleted,
padding: const EdgeInsets.symmetric(horizontal: 8.0),
showInChannelIndicator: showInChannelIndicator,
showThreadReplyIndicator: showThreadReplyIndicator,
showUsername: showUsername,
showTimestamp: showTimeStamp,
showSendingIndicator: showSendingIndicator,
showUserAvatar: showUserAvatar,
onQuotedMessageTap: (quotedMessageId) async {
final scrollToIndex = () {
final index = messages.indexWhere((m) => m.id == quotedMessageId);
@@ -814,17 +848,9 @@ class _MessageListViewState extends State<MessageListView> {
});
}
},
showInChannelIndicator: widget.parentMessage == null,
showThreadReplyIndicator: widget.parentMessage == null,
showUsername: !isMyMessage && (timeDiff >= 1 || !isNextUserSame),
showSendingIndicator:
isMyMessage && (index == 0 || timeDiff >= 1 || !isNextUserSame)
? DisplayWidget.show
: DisplayWidget.hide,
showTimestamp:
!isNextUserSame || readList?.isNotEmpty == true || timeDiff >= 1,
showEditMessage: isMyMessage,
showDeleteMessage: isMyMessage,
showThreadReplyMessage: !isThreadMessage,
showFlagButton: !isMyMessage,
borderSide: isMyMessage ? BorderSide.none : null,
onThreadTap: _onThreadTap,
@@ -843,11 +869,6 @@ class _MessageListViewState extends State<MessageListView> {
topRight: Radius.circular(16),
bottomRight: Radius.circular(16),
),
showUserAvatar: isMyMessage
? DisplayWidget.gone
: (timeDiff >= 1 || !isNextUserSame
? DisplayWidget.show
: DisplayWidget.hide),
messageTheme: isMyMessage
? StreamChatTheme.of(context).ownMessageTheme
: StreamChatTheme.of(context).otherMessageTheme,
@@ -856,7 +877,7 @@ class _MessageListViewState extends State<MessageListView> {
onShowMessage: widget.onShowMessage,
);
if (!isThreadMessage) {
if (!message.isDeleted && !message.isSystem && !message.isEphemeral) {
child = Swipeable(
onSwipeEnd: () => widget.onMessageSwiped(message),
backgroundIcon: StreamSvgIcon.reply(
@@ -919,7 +940,7 @@ class _MessageListViewState extends State<MessageListView> {
}
});
if (widget.parentMessage != null) {
if (_isThreadConversation) {
streamChannel.getReplies(widget.parentMessage.id);
}
+1 -1
View File
@@ -116,7 +116,7 @@ class MessageReactionsModal extends StatelessWidget {
showThreadReplyIndicator: false,
showTimestamp: false,
translateUserAvatar: false,
showSendingIndicator: DisplayWidget.gone,
showSendingIndicator: false,
shape: messageShape,
showInChannelIndicator: false,
showReactionPickerIndicator: showReactions &&
+123 -126
View File
@@ -1,7 +1,6 @@
import 'dart:math';
import 'dart:ui';
import 'package:emojis/emoji.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@@ -96,7 +95,7 @@ class MessageWidget extends StatefulWidget {
final DisplayWidget showUserAvatar;
/// It controls the display behaviour of the sending indicator
final DisplayWidget showSendingIndicator;
final bool showSendingIndicator;
/// If true the widget will show the reactions
final bool showReactions;
@@ -106,9 +105,6 @@ class MessageWidget extends StatefulWidget {
/// If true the widget will show the thread reply indicator
final bool showThreadReplyIndicator;
/// If true the widget will show the reply indicator
final bool showReplyIndicator;
/// If true the widget will show the show in channel indicator
final bool showInChannelIndicator;
@@ -121,9 +117,6 @@ class MessageWidget extends StatefulWidget {
/// Used in [MessageReactionsModal] and [MessageActionsModal]
final bool showReactionPickerIndicator;
/// If true the widget will show the resendMessage indicator
final bool showResendMessage;
final List<Read> readList;
final ShowMessageCallback onShowMessage;
@@ -131,8 +124,14 @@ class MessageWidget extends StatefulWidget {
/// If true show the users username next to the timestamp of the message
final bool showUsername;
final bool showTimestamp;
final bool showDeleteMessage;
final bool showReplyMessage;
final bool showThreadReplyMessage;
final bool showEditMessage;
final bool showCopyMessage;
final bool showDeleteMessage;
final bool showResendMessage;
final bool showFlagButton;
final Map<String, AttachmentBuilder> attachmentBuilders;
@@ -158,10 +157,9 @@ class MessageWidget extends StatefulWidget {
this.onMentionTap,
this.showReactionPickerIndicator = false,
this.showUserAvatar = DisplayWidget.show,
this.showSendingIndicator = DisplayWidget.show,
this.showSendingIndicator = true,
this.showThreadReplyIndicator = true,
this.showInChannelIndicator = true,
this.showReplyIndicator = true,
this.onReplyTap,
this.onThreadTap,
this.showUsername = true,
@@ -169,6 +167,10 @@ class MessageWidget extends StatefulWidget {
this.showReactions = true,
this.showDeleteMessage = true,
this.showEditMessage = true,
this.showReplyMessage = true,
this.showThreadReplyMessage = true,
this.showResendMessage = true,
this.showCopyMessage = true,
this.showFlagButton = true,
this.onUserAvatarTap,
this.onLinkTap,
@@ -177,7 +179,6 @@ class MessageWidget extends StatefulWidget {
this.editMessageInputBuilder,
this.textBuilder,
Map<String, AttachmentBuilder> customAttachmentBuilders,
this.showResendMessage = true,
this.readList,
this.padding,
this.textPadding = const EdgeInsets.symmetric(
@@ -241,18 +242,19 @@ class MessageWidget extends StatefulWidget {
}
class _MessageWidgetState extends State<MessageWidget> {
bool get showThreadReplyIndicator =>
widget.showThreadReplyIndicator && widget.message.replyCount > 0;
bool get showThreadReplyIndicator => widget.showThreadReplyIndicator;
bool get showSendingIndicator => widget.showSendingIndicator;
bool get isDeleted => widget.message.isDeleted;
bool get showUsername => widget.showUsername;
bool get showTimeStamp =>
widget.message.createdAt != null && widget.showTimestamp;
bool get showTimeStamp => widget.showTimestamp;
bool get isMessageRead => widget.readList?.isNotEmpty == true;
bool get showInChannel =>
widget.showInChannelIndicator && widget.message?.showInChannel == true;
bool get showInChannel => widget.showInChannelIndicator;
bool get hasQuotedMessage => widget.message?.quotedMessage != null;
@@ -274,7 +276,9 @@ class _MessageWidgetState extends State<MessageWidget> {
showThreadReplyIndicator ||
showUsername ||
showTimeStamp ||
showInChannel;
showInChannel ||
showSendingIndicator ||
isDeleted;
@override
Widget build(BuildContext context) {
@@ -282,9 +286,6 @@ class _MessageWidgetState extends State<MessageWidget> {
var leftPadding =
widget.showUserAvatar != DisplayWidget.gone ? avatarWidth + 8.5 : 0.5;
final isOnlyEmoji =
widget.message.text.characters.every((c) => Emoji.byChar(c) != null);
final hasFiles =
widget.message.attachments?.any((element) => element.type == 'file') ==
true;
@@ -452,7 +453,11 @@ class _MessageWidgetState extends State<MessageWidget> {
if (showBottomRow) SizedBox(height: 20.0),
],
),
if (showBottomRow) _buildBottomRow(leftPadding),
if (showBottomRow)
Padding(
padding: EdgeInsets.only(left: leftPadding),
child: _bottomRow,
),
if (isFailedState)
Positioned(
left: widget.reverse ? -3 : null,
@@ -481,12 +486,12 @@ class _MessageWidgetState extends State<MessageWidget> {
Widget _buildQuotedMessage() {
final isMyMessage =
widget.message.user.id == StreamChat.of(context).user.id;
final onTap = widget.message?.quotedMessage?.isDeleted != true &&
widget.onQuotedMessageTap != null
? () => widget.onQuotedMessageTap(widget.message.quotedMessageId)
: null;
return QuotedMessageWidget(
onTap: () {
if (widget.onQuotedMessageTap != null) {
widget.onQuotedMessageTap(widget.message.quotedMessageId);
}
},
onTap: onTap,
message: widget.message.quotedMessage,
messageTheme: isMyMessage
? StreamChatTheme.of(context).otherMessageTheme
@@ -495,12 +500,12 @@ class _MessageWidgetState extends State<MessageWidget> {
);
}
Widget _buildBottomRow(double leftPadding) {
final deleted = widget.message.isDeleted;
var children = <Widget>[];
if (deleted) {
children.add(
Row(
Widget get _bottomRow {
if (isDeleted) {
return Transform(
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
alignment: Alignment.center,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
StreamSvgIcon.eye(
@@ -518,102 +523,94 @@ class _MessageWidgetState extends State<MessageWidget> {
],
),
);
} else if (showInChannel) {
final onThreadTap = () async {
try {
}
var children = <Widget>[];
final threadParticipants = widget.message.threadParticipants;
final showThreadParticipants = threadParticipants?.isNotEmpty == true;
final replyCount = widget.message.replyCount;
var msg = 'Thread Reply';
if (showThreadReplyIndicator && replyCount > 1) {
msg = '$replyCount Thread Replies';
}
final onThreadTap = () async {
try {
var message = widget.message;
if (showInChannel) {
final channel = StreamChannel.of(context);
final message = await channel.getMessage(widget.message.parentId);
return widget.onThreadTap(message);
} catch (e, stk) {
print(e);
print(stk);
return null;
message = await channel.getMessage(widget.message.parentId);
}
};
children.add(
return widget.onThreadTap(message);
} catch (e, stk) {
print(e);
print(stk);
return null;
}
};
children.addAll([
if (showSendingIndicator) _buildSendingIndicator(),
if (showInChannel || showThreadReplyIndicator) ...[
if (showThreadParticipants)
SizedBox.fromSize(
size: Size((threadParticipants.length * 8.0) + 10, 16),
child: _buildThreadParticipantsIndicator(),
),
InkWell(
onTap: widget.onThreadTap != null ? onThreadTap : null,
child: Text('Thread Reply', style: widget.messageTheme?.replies),
child: Text(msg, style: widget.messageTheme?.replies),
),
);
} else {
final showSendingIndicator =
widget.showSendingIndicator == DisplayWidget.show;
final threadParticipants = widget.message.threadParticipants;
final showThreadParticipants = threadParticipants?.isNotEmpty == true;
final replyCount = widget.message.replyCount;
final msg = replyCount != 0
? '$replyCount ${replyCount > 1 ? 'Thread Replies' : 'Thread Reply'}'
: 'Thread Reply';
],
if (showUsername)
Text(
widget.message.user.name,
style: widget.messageTheme.replies.copyWith(
color: widget.messageTheme.createdAt.color,
),
),
if (showTimeStamp)
Text(
Jiffy(widget.message.createdAt.toLocal()).jm,
style: widget.messageTheme.createdAt,
),
]);
final onThreadTap = () async {
var message = widget.message;
return widget.onThreadTap(message);
};
children.addAll([
if (showSendingIndicator) _buildSendingIndicator(),
if (showThreadReplyIndicator) ...[
if (showThreadParticipants)
SizedBox.fromSize(
size: Size((threadParticipants.length * 8.0) + 10, 16),
child: _buildThreadParticipantsIndicator(),
),
InkWell(
onTap: widget.onThreadTap != null ? onThreadTap : null,
child: Text(msg, style: widget.messageTheme?.replies),
),
],
if (showUsername)
Text(
widget.message.user.name,
style: widget.messageTheme.replies.copyWith(
color: widget.messageTheme.createdAt.color,
),
),
if (showTimeStamp)
Text(
Jiffy(widget.message.createdAt.toLocal()).jm,
style: widget.messageTheme.createdAt,
),
]);
}
if (widget.reverse) children = children.reversed.toList();
return Padding(
padding: EdgeInsets.only(left: leftPadding),
child: Flex(
direction: Axis.horizontal,
clipBehavior: Clip.none,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (!deleted && (showThreadReplyIndicator || showInChannel))
Container(
margin: EdgeInsets.only(
bottom: widget.messageTheme.replies.fontSize / 2,
),
child: CustomPaint(
size: const Size(16, 32),
painter: _ThreadReplyPainter(
context: context,
color: widget.messageTheme.messageBorderColor,
),
),
return Flex(
direction: Axis.horizontal,
clipBehavior: Clip.none,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (showThreadReplyIndicator || showInChannel)
Container(
margin: EdgeInsets.only(
bottom: widget.messageTheme.replies.fontSize / 2,
),
...children.map(
(child) => Transform(
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
alignment: Alignment.center,
child: Container(
height: 16,
child: Center(
child: child,
),
child: CustomPaint(
size: const Size(16, 32),
painter: _ThreadReplyPainter(
context: context,
color: widget.messageTheme.messageBorderColor,
),
),
),
].insertBetween(const SizedBox(width: 8.0)),
),
...children.map(
(child) => Transform(
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
alignment: Alignment.center,
child: Container(
height: 16,
child: Center(
child: child,
),
),
),
),
].insertBetween(const SizedBox(width: 8.0)),
);
}
@@ -709,14 +706,15 @@ class _MessageWidgetState extends State<MessageWidget> {
messageTheme: widget.messageTheme,
messageShape: widget.shape ?? _getDefaultShape(context),
reverse: widget.reverse,
showDeleteMessage: widget.showDeleteMessage,
showDeleteMessage: widget.showDeleteMessage || isDeleteFailed,
message: widget.message,
editMessageInputBuilder: widget.editMessageInputBuilder,
onReplyTap: widget.onReplyTap,
onThreadReplyTap: widget.onThreadTap,
showResendMessage:
widget.showResendMessage && (isSendFailed || isUpdateFailed),
showCopyMessage: !isFailedState &&
showCopyMessage: widget.showCopyMessage &&
!isFailedState &&
widget.message.text?.trim()?.isNotEmpty == true,
showEditMessage: widget.showEditMessage &&
!isDeleteFailed &&
@@ -724,11 +722,12 @@ class _MessageWidgetState extends State<MessageWidget> {
?.any((element) => element.type == 'giphy') !=
true,
showReactions: widget.showReactions,
showReply: widget.showReplyIndicator &&
showReplyMessage: widget.showReplyMessage &&
!isFailedState &&
widget.onReplyTap != null,
showThreadReply:
widget.showThreadReplyIndicator && widget.onThreadTap != null,
showThreadReplyMessage: widget.showThreadReplyMessage &&
!isFailedState &&
widget.onThreadTap != null,
showFlagButton: widget.showFlagButton,
),
);
@@ -943,9 +942,7 @@ class _MessageWidgetState extends State<MessageWidget> {
);
}
bool get isOnlyEmoji =>
widget.message.text.characters.isNotEmpty &&
widget.message.text.characters.every((c) => Emoji.byChar(c) != null);
bool get isOnlyEmoji => textIsOnlyEmoji(widget.message.text);
Color _getBackgroundColor() {
if (hasQuotedMessage) {
+2 -4
View File
@@ -1,18 +1,17 @@
import 'dart:math';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:emojis/emoji.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:video_player/video_player.dart';
import 'attachment_error.dart';
import 'extension.dart';
import 'image_attachment.dart';
import 'message_text.dart';
import 'stream_chat_theme.dart';
import 'user_avatar.dart';
import 'utils.dart';
import 'extension.dart';
typedef QuotedMessageAttachmentThumbnailBuilder = Widget Function(
BuildContext,
@@ -126,8 +125,7 @@ class QuotedMessageWidget extends StatelessWidget {
}
Widget _buildMessage(BuildContext context) {
final isOnlyEmoji =
message.text.characters.every((c) => Emoji.byChar(c) != null);
final isOnlyEmoji = textIsOnlyEmoji(message.text);
var msg = _hasAttachments && !_containsText
? message.copyWith(text: message.attachments.last?.title ?? '')
: message;
+13 -1
View File
@@ -470,7 +470,19 @@ class StreamSvgIcon extends StatelessWidget {
);
}
factory StreamSvgIcon.icon_SHARE({
factory StreamSvgIcon.Icon_moon({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'icon_moon.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.Icon_SHARE({
double size,
Color color,
}) {
+2
View File
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'back_button.dart';
import 'channel_name.dart';
@@ -81,6 +82,7 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
elevation: 1,
leading: showBackButton
? StreamBackButton(
cid: StreamChannel.of(context).channel.cid,
onPressed: onBackPressed,
showUnreads: true,
)
+10 -2
View File
@@ -5,14 +5,22 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
class UnreadIndicator extends StatelessWidget {
const UnreadIndicator({
Key key,
this.cid,
}) : super(key: key);
/// Channel cid used to retrieve unread count
final String cid;
@override
Widget build(BuildContext context) {
final client = StreamChat.of(context).client;
return StreamBuilder<int>(
stream: client.state.totalUnreadCountStream,
initialData: client.state.totalUnreadCount,
stream: cid != null
? client.state.channels[cid].state.unreadCountStream
: client.state.totalUnreadCountStream,
initialData: cid != null
? client.state.channels[cid].state.unreadCount
: client.state.totalUnreadCount,
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data == 0) {
return SizedBox();
+15 -2
View File
@@ -1,10 +1,10 @@
import 'package:emojis/emoji.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:url_launcher/url_launcher.dart';
import 'stream_svg_icon.dart';
import '../stream_chat_flutter.dart';
import 'stream_svg_icon.dart';
Future<void> launchURL(BuildContext context, String url) async {
if (await canLaunch(url)) {
@@ -289,3 +289,16 @@ StreamSvgIcon getFileTypeImage(String type) {
break;
}
}
final _emojis = Emoji.all();
bool textIsOnlyEmoji(String text) {
return text.trim().characters.isNotEmpty &&
text.trim().characters.every((c) =>
_emojis.firstWhere(
(Emoji emoji) => emoji.char.contains(c),
orElse: () => null,
) !=
null) &&
text.characters.length < 4;
}