Merge branch 'develop' into perf
This commit is contained in:
@@ -27,6 +27,8 @@ class MessageActionsModal extends StatefulWidget {
|
||||
this.showResendMessage = true,
|
||||
this.showThreadReplyMessage = true,
|
||||
this.showFlagButton = true,
|
||||
this.showPinButton = true,
|
||||
this.showPinHighlight = false,
|
||||
this.showUserAvatar = DisplayWidget.show,
|
||||
this.editMessageInputBuilder,
|
||||
this.messageShape,
|
||||
@@ -35,6 +37,7 @@ class MessageActionsModal extends StatefulWidget {
|
||||
this.customActions = const [],
|
||||
this.attachmentBorderRadiusGeometry,
|
||||
this.onCopyTap,
|
||||
this.textBuilder,
|
||||
}) : super(key: key);
|
||||
|
||||
/// Builder for edit message
|
||||
@@ -79,6 +82,12 @@ class MessageActionsModal extends StatefulWidget {
|
||||
/// Flag for showing flag action
|
||||
final bool showFlagButton;
|
||||
|
||||
/// Flag for showing pin action
|
||||
final bool showPinButton;
|
||||
|
||||
/// Display Pin Highlight
|
||||
final bool showPinHighlight;
|
||||
|
||||
/// Flag for reversing message
|
||||
final bool reverse;
|
||||
|
||||
@@ -97,6 +106,9 @@ class MessageActionsModal extends StatefulWidget {
|
||||
/// List of custom actions
|
||||
final List<MessageAction> customActions;
|
||||
|
||||
/// Customize the MessageWidget textBuilder
|
||||
final Widget Function(BuildContext context, Message message)? textBuilder;
|
||||
|
||||
@override
|
||||
_MessageActionsModalState createState() => _MessageActionsModalState();
|
||||
}
|
||||
@@ -222,6 +234,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
showSendingIndicator: false,
|
||||
shape: widget.messageShape,
|
||||
attachmentShape: widget.attachmentShape,
|
||||
showPinHighlight: false,
|
||||
textBuilder: widget.textBuilder,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -258,6 +272,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
_buildCopyButton(context),
|
||||
if (widget.showFlagButton)
|
||||
_buildFlagButton(context),
|
||||
if (widget.showPinButton)
|
||||
_buildPinButton(context),
|
||||
if (widget.showDeleteMessage)
|
||||
_buildDeleteButton(context),
|
||||
...widget.customActions
|
||||
@@ -359,6 +375,21 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
}
|
||||
}
|
||||
|
||||
void _togglePin() async {
|
||||
final channel = StreamChannel.of(context).channel;
|
||||
|
||||
try {
|
||||
if (!widget.message.pinned) {
|
||||
await channel.pinMessage(widget.message);
|
||||
} else {
|
||||
await channel.unpinMessage(widget.message);
|
||||
}
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
_showErrorAlert();
|
||||
}
|
||||
}
|
||||
|
||||
void _showDeleteDialog() async {
|
||||
setState(() {
|
||||
_showActions = false;
|
||||
@@ -451,6 +482,29 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPinButton(BuildContext context) {
|
||||
final streamChatThemeData = StreamChatTheme.of(context);
|
||||
return InkWell(
|
||||
onTap: _togglePin,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
StreamSvgIcon.pin(
|
||||
color: streamChatThemeData.primaryIconTheme.color,
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'${widget.message.pinned ? 'Unpin from' : 'Pin to'} Conversation',
|
||||
style: streamChatThemeData.textTheme.body,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDeleteButton(BuildContext context) {
|
||||
final isDeleteFailed =
|
||||
widget.message.status == MessageSendingStatus.failed_delete;
|
||||
|
||||
@@ -156,8 +156,10 @@ class MessageListView extends StatefulWidget {
|
||||
this.onMessageTap,
|
||||
this.onSystemMessageTap,
|
||||
this.onAttachmentTap,
|
||||
this.textBuilder,
|
||||
this.onLinkTap,
|
||||
this.pinPermissions = const [],
|
||||
this.textBuilder,
|
||||
this.usernameBuilder,
|
||||
}) : super(key: key);
|
||||
|
||||
/// Function used to build a custom message widget
|
||||
@@ -259,11 +261,17 @@ class MessageListView extends StatefulWidget {
|
||||
final void Function(Message message, Attachment attachment)? onAttachmentTap;
|
||||
|
||||
/// Customize the MessageWidget textBuilder
|
||||
final void Function(BuildContext context, Message message)? textBuilder;
|
||||
final Widget Function(BuildContext context, Message message)? textBuilder;
|
||||
|
||||
/// Customize the MessageWidget usernameBuilder
|
||||
final Widget Function(BuildContext context, Message message)? usernameBuilder;
|
||||
|
||||
/// Callback for when link is tapped
|
||||
final void Function(String link)? onLinkTap;
|
||||
|
||||
/// A List of user types that have permission to pin messages
|
||||
final List<String> pinPermissions;
|
||||
|
||||
@override
|
||||
_MessageListViewState createState() => _MessageListViewState();
|
||||
}
|
||||
@@ -805,6 +813,10 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
) {
|
||||
final isMyMessage = message.user!.id == StreamChat.of(context).user!.id;
|
||||
final isOnlyEmoji = message.text!.isOnlyEmoji;
|
||||
final currentUser = StreamChat.of(context).user;
|
||||
final members = StreamChannel.of(context).channel.state?.members ?? [];
|
||||
final currentUserMember =
|
||||
members.firstWhere((e) => e.user!.id == currentUser!.id);
|
||||
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return MessageWidget(
|
||||
@@ -855,9 +867,10 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
}
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
textBuilder:
|
||||
widget.textBuilder as Widget Function(BuildContext, Message)?,
|
||||
textBuilder: widget.textBuilder,
|
||||
usernameBuilder: widget.usernameBuilder,
|
||||
onLinkTap: widget.onLinkTap,
|
||||
showPinButton: widget.pinPermissions.contains(currentUserMember.role),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -944,6 +957,11 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
? BorderSide.none
|
||||
: null;
|
||||
|
||||
final currentUser = StreamChat.of(context).user;
|
||||
final members = StreamChannel.of(context).channel.state?.members ?? [];
|
||||
final currentUserMember =
|
||||
members.firstWhere((e) => e.user!.id == currentUser!.id);
|
||||
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
Widget child = MessageWidget(
|
||||
key: ValueKey<String>('MESSAGE-${message.id}'),
|
||||
@@ -1054,9 +1072,10 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
onAttachmentTap: widget.onAttachmentTap,
|
||||
textBuilder:
|
||||
widget.textBuilder as Widget Function(BuildContext, Message)?,
|
||||
textBuilder: widget.textBuilder,
|
||||
usernameBuilder: widget.usernameBuilder,
|
||||
onLinkTap: widget.onLinkTap,
|
||||
showPinButton: widget.pinPermissions.contains(currentUserMember.role),
|
||||
);
|
||||
|
||||
if (!message.isDeleted &&
|
||||
|
||||
@@ -23,6 +23,7 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
this.showUserAvatar = DisplayWidget.show,
|
||||
this.onUserAvatarTap,
|
||||
this.attachmentBorderRadiusGeometry,
|
||||
this.textBuilder,
|
||||
}) : super(key: key);
|
||||
|
||||
/// Message to display reactions of
|
||||
@@ -52,6 +53,9 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
/// [BorderRadius] to apply to attachments
|
||||
final BorderRadius? attachmentBorderRadiusGeometry;
|
||||
|
||||
/// Customize the MessageWidget textBuilder
|
||||
final Widget Function(BuildContext context, Message message)? textBuilder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = MediaQuery.of(context).size;
|
||||
@@ -157,6 +161,8 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
),
|
||||
showReactionPickerIndicator: showReactions &&
|
||||
(message.status == MessageSendingStatus.sent),
|
||||
textBuilder: textBuilder,
|
||||
showPinHighlight: false,
|
||||
),
|
||||
),
|
||||
if (message.latestReactions?.isNotEmpty == true) ...[
|
||||
|
||||
@@ -83,6 +83,8 @@ class MessageWidget extends StatefulWidget {
|
||||
this.showResendMessage = true,
|
||||
this.showCopyMessage = true,
|
||||
this.showFlagButton = true,
|
||||
this.showPinButton = true,
|
||||
this.showPinHighlight = true,
|
||||
this.onUserAvatarTap,
|
||||
this.onLinkTap,
|
||||
this.onMessageActions,
|
||||
@@ -102,6 +104,7 @@ class MessageWidget extends StatefulWidget {
|
||||
this.onQuotedMessageTap,
|
||||
this.customActions = const [],
|
||||
this.onAttachmentTap,
|
||||
this.usernameBuilder,
|
||||
}) : attachmentBuilders = {
|
||||
'image': (context, message, attachments) {
|
||||
final border = RoundedRectangleBorder(
|
||||
@@ -265,6 +268,9 @@ class MessageWidget extends StatefulWidget {
|
||||
/// Widget builder for building text
|
||||
final Widget Function(BuildContext, Message)? textBuilder;
|
||||
|
||||
/// Widget builder for building username
|
||||
final Widget Function(BuildContext, Message)? usernameBuilder;
|
||||
|
||||
/// Function called on long press
|
||||
final void Function(BuildContext, Message)? onMessageActions;
|
||||
|
||||
@@ -367,6 +373,12 @@ class MessageWidget extends StatefulWidget {
|
||||
/// Show flag action
|
||||
final bool showFlagButton;
|
||||
|
||||
/// Show flag action
|
||||
final bool showPinButton;
|
||||
|
||||
/// Display Pin Highlight
|
||||
final bool showPinHighlight;
|
||||
|
||||
/// Builder for respective attachment types
|
||||
final Map<String, AttachmentBuilder> attachmentBuilders;
|
||||
|
||||
@@ -450,7 +462,12 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
widget.showUserAvatar != DisplayWidget.gone ? avatarWidth + 8.5 : 0.5;
|
||||
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
type: widget.message.pinned && widget.showPinHighlight
|
||||
? MaterialType.card
|
||||
: MaterialType.transparency,
|
||||
color: widget.message.pinned && widget.showPinHighlight
|
||||
? StreamChatTheme.of(context).colorTheme.highlight
|
||||
: null,
|
||||
child: Portal(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
@@ -477,148 +494,165 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
? AlignmentDirectional.bottomEnd
|
||||
: AlignmentDirectional.bottomStart,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: widget.reverse
|
||||
? CrossAxisAlignment.end
|
||||
: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
if (widget.showUserAvatar == DisplayWidget.show &&
|
||||
widget.message.user != null) ...[
|
||||
_buildUserAvatar(),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
if (widget.showUserAvatar == DisplayWidget.hide)
|
||||
SizedBox(width: avatarWidth + 4),
|
||||
Flexible(
|
||||
child: PortalEntry(
|
||||
portal: Container(
|
||||
transform: Matrix4.translationValues(
|
||||
widget.reverse ? 12 : -12, 0, 0),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 22 * 6.0),
|
||||
child: _buildReactionIndicator(context),
|
||||
),
|
||||
portalAnchor:
|
||||
Alignment(widget.reverse ? 1 : -1, -1),
|
||||
childAnchor:
|
||||
Alignment(widget.reverse ? -1 : 1, -1),
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Padding(
|
||||
padding: widget.showReactions
|
||||
? EdgeInsets.only(
|
||||
top: widget
|
||||
.message
|
||||
.reactionCounts
|
||||
?.isNotEmpty ==
|
||||
true
|
||||
? 18
|
||||
: 0,
|
||||
)
|
||||
: EdgeInsets.zero,
|
||||
child: (widget.message.isDeleted &&
|
||||
!isFailedState)
|
||||
? Container(
|
||||
// ignore: lines_longer_than_80_chars
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal:
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom:
|
||||
isPinned && widget.showPinHighlight ? 8.0 : 0.0,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: widget.reverse
|
||||
? CrossAxisAlignment.end
|
||||
: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (widget.message.pinned &&
|
||||
widget.message.pinnedBy != null &&
|
||||
widget.showPinHighlight)
|
||||
_buildPinnedMessage(widget.message),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
if (widget.showUserAvatar ==
|
||||
DisplayWidget.show &&
|
||||
widget.message.user != null) ...[
|
||||
_buildUserAvatar(),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
if (widget.showUserAvatar == DisplayWidget.hide)
|
||||
SizedBox(width: avatarWidth + 4),
|
||||
Flexible(
|
||||
child: PortalEntry(
|
||||
portal: Container(
|
||||
transform: Matrix4.translationValues(
|
||||
widget.reverse ? 12 : -12, 0, 0),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 22 * 6.0),
|
||||
child: _buildReactionIndicator(context),
|
||||
),
|
||||
portalAnchor:
|
||||
Alignment(widget.reverse ? 1 : -1, -1),
|
||||
childAnchor:
|
||||
Alignment(widget.reverse ? -1 : 1, -1),
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Padding(
|
||||
padding: widget.showReactions
|
||||
? EdgeInsets.only(
|
||||
top: widget
|
||||
.message
|
||||
.reactionCounts
|
||||
?.isNotEmpty ==
|
||||
true
|
||||
? 18
|
||||
: 0,
|
||||
)
|
||||
: EdgeInsets.zero,
|
||||
child: (widget.message.isDeleted &&
|
||||
!isFailedState)
|
||||
? Container(
|
||||
// ignore: lines_longer_than_80_chars
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal:
|
||||
// ignore: lines_longer_than_80_chars
|
||||
widget.showUserAvatar ==
|
||||
// ignore: lines_longer_than_80_chars
|
||||
DisplayWidget.gone
|
||||
? 0
|
||||
: 4.0),
|
||||
child: DeletedMessage(
|
||||
borderRadiusGeometry: widget
|
||||
.borderRadiusGeometry,
|
||||
borderSide:
|
||||
widget.borderSide,
|
||||
shape: widget.shape,
|
||||
messageTheme:
|
||||
widget.messageTheme,
|
||||
),
|
||||
)
|
||||
: Card(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
elevation: 0,
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: (isFailedState
|
||||
? 15.0
|
||||
: 0.0) +
|
||||
// ignore: lines_longer_than_80_chars
|
||||
widget.showUserAvatar ==
|
||||
// ignore: lines_longer_than_80_chars
|
||||
(widget.showUserAvatar ==
|
||||
DisplayWidget
|
||||
.gone
|
||||
? 0
|
||||
: 4.0),
|
||||
child: DeletedMessage(
|
||||
borderRadiusGeometry: widget
|
||||
.borderRadiusGeometry,
|
||||
borderSide: widget.borderSide,
|
||||
shape: widget.shape,
|
||||
messageTheme:
|
||||
widget.messageTheme,
|
||||
),
|
||||
)
|
||||
: Card(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
elevation: 0,
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: (isFailedState
|
||||
? 15.0
|
||||
: 0.0) +
|
||||
// ignore: lines_longer_than_80_chars
|
||||
(widget.showUserAvatar ==
|
||||
DisplayWidget.gone
|
||||
? 0
|
||||
: 4.0),
|
||||
),
|
||||
shape: widget.shape ??
|
||||
RoundedRectangleBorder(
|
||||
side: widget.borderSide ??
|
||||
BorderSide(
|
||||
color: widget
|
||||
// ignore: lines_longer_than_80_chars
|
||||
.messageTheme
|
||||
// ignore: lines_longer_than_80_chars
|
||||
.messageBorderColor ??
|
||||
Colors.grey,
|
||||
),
|
||||
borderRadius: widget
|
||||
// ignore: lines_longer_than_80_chars
|
||||
.borderRadiusGeometry ??
|
||||
BorderRadius.zero,
|
||||
),
|
||||
color: _getBackgroundColor(),
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.end,
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
if (hasQuotedMessage)
|
||||
_buildQuotedMessage(),
|
||||
if (hasNonUrlAttachments)
|
||||
_parseAttachments(),
|
||||
if (!isGiphy)
|
||||
_buildTextBubble(),
|
||||
],
|
||||
),
|
||||
shape: widget.shape ??
|
||||
RoundedRectangleBorder(
|
||||
side: widget
|
||||
.borderSide ??
|
||||
BorderSide(
|
||||
color: widget
|
||||
// ignore: lines_longer_than_80_chars
|
||||
.messageTheme
|
||||
// ignore: lines_longer_than_80_chars
|
||||
.messageBorderColor ??
|
||||
Colors.grey,
|
||||
),
|
||||
borderRadius: widget
|
||||
// ignore: lines_longer_than_80_chars
|
||||
.borderRadiusGeometry ??
|
||||
BorderRadius.zero,
|
||||
),
|
||||
color: _getBackgroundColor(),
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.end,
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
if (hasQuotedMessage)
|
||||
_buildQuotedMessage(),
|
||||
if (hasNonUrlAttachments)
|
||||
_parseAttachments(),
|
||||
if (!isGiphy)
|
||||
_buildTextBubble(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.showReactionPickerIndicator)
|
||||
Positioned(
|
||||
right: widget.reverse ? null : 4,
|
||||
left: widget.reverse ? 4 : null,
|
||||
top: -8,
|
||||
child: CustomPaint(
|
||||
painter: ReactionBubblePainter(
|
||||
StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
Colors.transparent,
|
||||
Colors.transparent,
|
||||
tailCirclesSpace: 1,
|
||||
),
|
||||
),
|
||||
if (widget.showReactionPickerIndicator)
|
||||
Positioned(
|
||||
right: widget.reverse ? null : 4,
|
||||
left: widget.reverse ? 4 : null,
|
||||
top: -8,
|
||||
child: CustomPaint(
|
||||
painter: ReactionBubblePainter(
|
||||
StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
Colors.transparent,
|
||||
Colors.transparent,
|
||||
tailCirclesSpace: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (showBottomRow)
|
||||
SizedBox(height: context.textScaleFactor * 18.0),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (showBottomRow)
|
||||
SizedBox(height: context.textScaleFactor * 18.0),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (showBottomRow)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: leftPadding),
|
||||
padding: EdgeInsets.only(
|
||||
left: leftPadding,
|
||||
bottom:
|
||||
isPinned && widget.showPinHighlight ? 6.0 : 0.0,
|
||||
),
|
||||
child: _bottomRow,
|
||||
),
|
||||
if (isFailedState)
|
||||
@@ -721,14 +755,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
child: Text(msg, style: widget.messageTheme.replies),
|
||||
),
|
||||
],
|
||||
if (showUsername)
|
||||
Text(
|
||||
widget.message.user!.name,
|
||||
maxLines: 1,
|
||||
key: usernameKey,
|
||||
style: widget.messageTheme.messageAuthor,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (showUsername) _buildUsername(usernameKey),
|
||||
if (showTimeStamp)
|
||||
Text(
|
||||
Jiffy(widget.message.createdAt.toLocal()).jm,
|
||||
@@ -791,6 +818,19 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildUsername(Key usernameKey) {
|
||||
if (widget.usernameBuilder != null) {
|
||||
return widget.usernameBuilder!(context, widget.message);
|
||||
}
|
||||
return Text(
|
||||
widget.message.user!.name,
|
||||
maxLines: 1,
|
||||
key: usernameKey,
|
||||
style: widget.messageTheme.messageAuthor,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildUrlAttachment() {
|
||||
final urlAttachment = widget.message.attachments
|
||||
.firstWhere((element) => element.ogScrapeUrl != null);
|
||||
@@ -882,6 +922,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
builder: (context) => StreamChannel(
|
||||
channel: channel,
|
||||
child: MessageActionsModal(
|
||||
textBuilder: widget.textBuilder,
|
||||
onCopyTap: (message) =>
|
||||
Clipboard.setData(ClipboardData(text: message.text)),
|
||||
attachmentBorderRadiusGeometry:
|
||||
@@ -918,6 +959,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
!isFailedState &&
|
||||
widget.onThreadTap != null,
|
||||
showFlagButton: widget.showFlagButton,
|
||||
showPinButton: widget.showPinButton,
|
||||
customActions: widget.customActions,
|
||||
),
|
||||
));
|
||||
@@ -931,6 +973,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
builder: (context) => StreamChannel(
|
||||
channel: channel,
|
||||
child: MessageReactionsModal(
|
||||
textBuilder: widget.textBuilder,
|
||||
attachmentBorderRadiusGeometry:
|
||||
widget.attachmentBorderRadiusGeometry as BorderRadius?,
|
||||
showUserAvatar:
|
||||
@@ -1113,8 +1156,38 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPinnedMessage(Message message) {
|
||||
final pinnedBy = message.pinnedBy;
|
||||
final pinnedByMe = StreamChat.of(context).user!.id == pinnedBy!.id;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
StreamSvgIcon.pin(
|
||||
size: 16,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 4,
|
||||
),
|
||||
Text(
|
||||
'Pinned by ${pinnedByMe ? 'You' : pinnedBy.name}',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
bool get isOnlyEmoji => widget.message.text!.isOnlyEmoji;
|
||||
|
||||
bool get isPinned => widget.message.pinned;
|
||||
|
||||
Color? _getBackgroundColor() {
|
||||
if (hasQuotedMessage) {
|
||||
return widget.messageTheme.messageBackgroundColor;
|
||||
|
||||
@@ -889,6 +889,18 @@ class StreamSvgIcon extends StatelessWidget {
|
||||
height: size,
|
||||
);
|
||||
|
||||
/// [StreamSvgIcon] type
|
||||
factory StreamSvgIcon.pin({
|
||||
double? size,
|
||||
Color? color,
|
||||
}) =>
|
||||
StreamSvgIcon(
|
||||
assetName: 'icon_pin.svg',
|
||||
color: color,
|
||||
width: size,
|
||||
height: size,
|
||||
);
|
||||
|
||||
/// Name of icon asset
|
||||
final String? assetName;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user