Merge branch 'develop' into feat/pinned

This commit is contained in:
Salvatore Giordano
2021-06-08 09:43:07 +02:00
12 changed files with 167 additions and 110 deletions
+11 -2
View File
@@ -351,9 +351,13 @@ class Channel {
} }
/// Send a [message] to this channel. /// Send a [message] to this channel.
/// If [skipPush] is true the message will not send a push notification
/// Waits for a [_messageAttachmentsUploadCompleter] to complete /// Waits for a [_messageAttachmentsUploadCompleter] to complete
/// before actually sending the message. /// before actually sending the message.
Future<SendMessageResponse> sendMessage(Message message) async { Future<SendMessageResponse> sendMessage(
Message message, {
bool skipPush = false,
}) async {
_checkInitialized(); _checkInitialized();
// Cancelling previous completer in case it's called again in the process // Cancelling previous completer in case it's called again in the process
// Eg. Updating the message while the previous call is in progress. // Eg. Updating the message while the previous call is in progress.
@@ -396,7 +400,12 @@ class Channel {
message = await attachmentsUploadCompleter.future; message = await attachmentsUploadCompleter.future;
} }
final response = await _client.sendMessage(message, id!, type); final response = await _client.sendMessage(
message,
id!,
type,
skipPush: skipPush,
);
state!.addMessage(response.message); state!.addMessage(response.message);
return response; return response;
} catch (error) { } catch (error) {
+7 -3
View File
@@ -1329,11 +1329,15 @@ class StreamChatClient {
Future<SendMessageResponse> sendMessage( Future<SendMessageResponse> sendMessage(
Message message, Message message,
String channelId, String channelId,
String channelType, String channelType, {
) async { bool skipPush = false,
}) async {
final response = await post( final response = await post(
'/channels/$channelType/$channelId/message', '/channels/$channelType/$channelId/message',
data: {'message': message.toJson()}, data: {
'message': message.toJson(),
'skip_push': skipPush,
},
); );
return decode(response.data, SendMessageResponse.fromJson); return decode(response.data, SendMessageResponse.fromJson);
} }
@@ -73,7 +73,6 @@ class Message extends Equatable {
this.extraData = const {}, this.extraData = const {},
this.deletedAt, this.deletedAt,
this.status = MessageSendingStatus.sent, this.status = MessageSendingStatus.sent,
this.skipPush = false,
}) : id = id ?? const Uuid().v4(), }) : id = id ?? const Uuid().v4(),
pinExpires = pinExpires?.toUtc(), pinExpires = pinExpires?.toUtc(),
createdAt = createdAt ?? DateTime.now(), createdAt = createdAt ?? DateTime.now(),
@@ -158,10 +157,6 @@ class Message extends Equatable {
@JsonKey(defaultValue: false) @JsonKey(defaultValue: false)
final bool silent; final bool silent;
/// If true the message will not send a push notification
@JsonKey(defaultValue: false)
final bool skipPush;
/// If true the message is shadowed /// If true the message is shadowed
@JsonKey( @JsonKey(
includeIfNull: false, includeIfNull: false,
@@ -253,7 +248,6 @@ class Message extends Equatable {
'pinned_at', 'pinned_at',
'pin_expires', 'pin_expires',
'pinned_by', 'pinned_by',
'skip_push',
]; ];
/// Serialize to json /// Serialize to json
@@ -291,7 +285,6 @@ class Message extends Equatable {
User? pinnedBy, User? pinnedBy,
Map<String, Object?>? extraData, Map<String, Object?>? extraData,
MessageSendingStatus? status, MessageSendingStatus? status,
bool? skipPush,
}) { }) {
assert(() { assert(() {
if (pinExpires is! DateTime && if (pinExpires is! DateTime &&
@@ -331,7 +324,6 @@ class Message extends Equatable {
pinnedBy: pinnedBy ?? this.pinnedBy, pinnedBy: pinnedBy ?? this.pinnedBy,
pinExpires: pinExpires:
pinExpires == _pinExpires ? this.pinExpires : pinExpires as DateTime?, pinExpires == _pinExpires ? this.pinExpires : pinExpires as DateTime?,
skipPush: skipPush ?? this.skipPush,
); );
} }
@@ -398,7 +390,6 @@ class Message extends Equatable {
pinnedBy, pinnedBy,
extraData, extraData,
status, status,
skipPush,
]; ];
} }
@@ -67,7 +67,6 @@ Message _$MessageFromJson(Map<String, dynamic> json) {
deletedAt: json['deleted_at'] == null deletedAt: json['deleted_at'] == null
? null ? null
: DateTime.parse(json['deleted_at'] as String), : DateTime.parse(json['deleted_at'] as String),
skipPush: json['skip_push'] as bool? ?? false,
); );
} }
@@ -97,7 +96,6 @@ Map<String, dynamic> _$MessageToJson(Message instance) {
writeNotNull('thread_participants', readonly(instance.threadParticipants)); writeNotNull('thread_participants', readonly(instance.threadParticipants));
val['show_in_channel'] = instance.showInChannel; val['show_in_channel'] = instance.showInChannel;
val['silent'] = instance.silent; val['silent'] = instance.silent;
val['skip_push'] = instance.skipPush;
writeNotNull('shadowed', readonly(instance.shadowed)); writeNotNull('shadowed', readonly(instance.shadowed));
writeNotNull('command', readonly(instance.command)); writeNotNull('command', readonly(instance.command));
writeNotNull('created_at', readonly(instance.createdAt)); writeNotNull('created_at', readonly(instance.createdAt));
@@ -52,7 +52,10 @@ void main() {
when( when(
() => mockDio.post<String>( () => mockDio.post<String>(
'/channels/messaging/testid/message', '/channels/messaging/testid/message',
data: {'message': message.toJson()}, data: {
'message': message.toJson(),
'skip_push': false,
},
), ),
).thenAnswer( ).thenAnswer(
(_) async => Response( (_) async => Response(
@@ -67,6 +70,7 @@ void main() {
verify(() => verify(() =>
mockDio.post<String>('/channels/messaging/testid/message', data: { mockDio.post<String>('/channels/messaging/testid/message', data: {
'message': message.toJson(), 'message': message.toJson(),
'skip_push': false,
})).called(1); })).called(1);
}); });
@@ -902,7 +902,6 @@ void main() {
"show_in_channel": null, "show_in_channel": null,
"mentioned_users": [], "mentioned_users": [],
"status": "SENT", "status": "SENT",
"skip_push": false,
"silent": false, "silent": false,
"pinned": false, "pinned": false,
"pinned_at": null, "pinned_at": null,
@@ -919,7 +918,6 @@ void main() {
"show_in_channel": null, "show_in_channel": null,
"mentioned_users": [], "mentioned_users": [],
"status": "SENT", "status": "SENT",
"skip_push": false,
"silent": false, "silent": false,
"pinned": false, "pinned": false,
"pinned_at": null, "pinned_at": null,
@@ -929,7 +927,6 @@ void main() {
{ {
"id": "dry-meadow-0-53e6299f-9b97-4a9c-a27e-7e2dde49b7e0", "id": "dry-meadow-0-53e6299f-9b97-4a9c-a27e-7e2dde49b7e0",
"text": "test message", "text": "test message",
"skip_push": false,
"attachments": [], "attachments": [],
"parent_id": null, "parent_id": null,
"quoted_message": null, "quoted_message": null,
@@ -952,7 +949,6 @@ void main() {
"quoted_message_id": null, "quoted_message_id": null,
"show_in_channel": null, "show_in_channel": null,
"mentioned_users": [], "mentioned_users": [],
"skip_push": false,
"status": "SENT", "status": "SENT",
"silent": false, "silent": false,
"pinned": false, "pinned": false,
@@ -964,7 +960,6 @@ void main() {
"id": "dry-meadow-0-64d7970f-ede8-4b31-9738-1bc1756d2bfe", "id": "dry-meadow-0-64d7970f-ede8-4b31-9738-1bc1756d2bfe",
"text": "test", "text": "test",
"attachments": [], "attachments": [],
"skip_push": false,
"parent_id": null, "parent_id": null,
"quoted_message": null, "quoted_message": null,
"quoted_message_id": null, "quoted_message_id": null,
@@ -982,7 +977,6 @@ void main() {
"text": "hi", "text": "hi",
"attachments": [], "attachments": [],
"parent_id": null, "parent_id": null,
"skip_push": false,
"quoted_message": null, "quoted_message": null,
"quoted_message_id": null, "quoted_message_id": null,
"show_in_channel": null, "show_in_channel": null,
@@ -1000,7 +994,6 @@ void main() {
"attachments": [], "attachments": [],
"parent_id": null, "parent_id": null,
"quoted_message": null, "quoted_message": null,
"skip_push": false,
"quoted_message_id": null, "quoted_message_id": null,
"show_in_channel": null, "show_in_channel": null,
"mentioned_users": [], "mentioned_users": [],
@@ -1023,7 +1016,6 @@ void main() {
"status": "SENT", "status": "SENT",
"silent": false, "silent": false,
"pinned": false, "pinned": false,
"skip_push": false,
"pinned_at": null, "pinned_at": null,
"pin_expires": null, "pin_expires": null,
"pinned_by": null "pinned_by": null
@@ -1041,7 +1033,6 @@ void main() {
"silent": false, "silent": false,
"pinned": false, "pinned": false,
"pinned_at": null, "pinned_at": null,
"skip_push": false,
"pin_expires": null, "pin_expires": null,
"pinned_by": null "pinned_by": null
}, },
@@ -1053,7 +1044,6 @@ void main() {
"quoted_message": null, "quoted_message": null,
"quoted_message_id": null, "quoted_message_id": null,
"show_in_channel": null, "show_in_channel": null,
"skip_push": false,
"mentioned_users": [], "mentioned_users": [],
"status": "SENT", "status": "SENT",
"silent": false, "silent": false,
@@ -1071,7 +1061,6 @@ void main() {
"quoted_message_id": null, "quoted_message_id": null,
"show_in_channel": null, "show_in_channel": null,
"mentioned_users": [], "mentioned_users": [],
"skip_push": false,
"status": "SENT", "status": "SENT",
"silent": false, "silent": false,
"pinned": false, "pinned": false,
@@ -1090,7 +1079,6 @@ void main() {
"mentioned_users": [], "mentioned_users": [],
"status": "SENT", "status": "SENT",
"silent": false, "silent": false,
"skip_push": false,
"pinned": false, "pinned": false,
"pinned_at": null, "pinned_at": null,
"pin_expires": null, "pin_expires": null,
@@ -1100,7 +1088,6 @@ void main() {
"id": "icy-recipe-7-935c396e-ddf8-4a9a-951c-0a12fa5bf055", "id": "icy-recipe-7-935c396e-ddf8-4a9a-951c-0a12fa5bf055",
"text": "what are you doing?", "text": "what are you doing?",
"attachments": [], "attachments": [],
"skip_push": false,
"parent_id": null, "parent_id": null,
"quoted_message": null, "quoted_message": null,
"quoted_message_id": null, "quoted_message_id": null,
@@ -1118,7 +1105,6 @@ void main() {
"text": "👍", "text": "👍",
"attachments": [], "attachments": [],
"parent_id": null, "parent_id": null,
"skip_push": false,
"quoted_message": null, "quoted_message": null,
"quoted_message_id": null, "quoted_message_id": null,
"show_in_channel": null, "show_in_channel": null,
@@ -1134,7 +1120,6 @@ void main() {
"id": "snowy-credit-3-3e0c1a0d-d22f-42ee-b2a1-f9f49477bf21", "id": "snowy-credit-3-3e0c1a0d-d22f-42ee-b2a1-f9f49477bf21",
"text": "sdasas", "text": "sdasas",
"attachments": [], "attachments": [],
"skip_push": false,
"parent_id": null, "parent_id": null,
"quoted_message": null, "quoted_message": null,
"quoted_message_id": null, "quoted_message_id": null,
@@ -1155,7 +1140,6 @@ void main() {
"quoted_message": null, "quoted_message": null,
"quoted_message_id": null, "quoted_message_id": null,
"show_in_channel": null, "show_in_channel": null,
"skip_push": false,
"mentioned_users": [], "mentioned_users": [],
"status": "SENT", "status": "SENT",
"silent": false, "silent": false,
@@ -1168,7 +1152,6 @@ void main() {
"id": "snowy-credit-3-cfaf0b46-1daa-49c5-947c-b16d6697487d", "id": "snowy-credit-3-cfaf0b46-1daa-49c5-947c-b16d6697487d",
"text": "nhisagdhsadz", "text": "nhisagdhsadz",
"attachments": [], "attachments": [],
"skip_push": false,
"parent_id": null, "parent_id": null,
"quoted_message": null, "quoted_message": null,
"quoted_message_id": null, "quoted_message_id": null,
@@ -1187,7 +1170,6 @@ void main() {
"attachments": [], "attachments": [],
"parent_id": null, "parent_id": null,
"quoted_message": null, "quoted_message": null,
"skip_push": false,
"quoted_message_id": null, "quoted_message_id": null,
"show_in_channel": null, "show_in_channel": null,
"mentioned_users": [], "mentioned_users": [],
@@ -1204,7 +1186,6 @@ void main() {
"attachments": [], "attachments": [],
"parent_id": null, "parent_id": null,
"quoted_message": null, "quoted_message": null,
"skip_push": false,
"quoted_message_id": null, "quoted_message_id": null,
"show_in_channel": null, "show_in_channel": null,
"mentioned_users": [], "mentioned_users": [],
@@ -1212,7 +1193,6 @@ void main() {
"silent": false, "silent": false,
"pinned": false, "pinned": false,
"pinned_at": null, "pinned_at": null,
"skip_push": false,
"pin_expires": null, "pin_expires": null,
"pinned_by": null "pinned_by": null
}, },
@@ -1229,7 +1209,6 @@ void main() {
"silent": false, "silent": false,
"pinned": false, "pinned": false,
"pinned_at": null, "pinned_at": null,
"skip_push": false,
"pin_expires": null, "pin_expires": null,
"pinned_by": null "pinned_by": null
}, },
@@ -1246,7 +1225,6 @@ void main() {
"silent": false, "silent": false,
"pinned": false, "pinned": false,
"pinned_at": null, "pinned_at": null,
"skip_push": false,
"pin_expires": null, "pin_expires": null,
"pinned_by": null "pinned_by": null
}, },
@@ -1263,7 +1241,6 @@ void main() {
"silent": false, "silent": false,
"pinned": false, "pinned": false,
"pinned_at": null, "pinned_at": null,
"skip_push": false,
"pin_expires": null, "pin_expires": null,
"pinned_by": null "pinned_by": null
}, },
@@ -1280,7 +1257,6 @@ void main() {
"silent": false, "silent": false,
"pinned": false, "pinned": false,
"pinned_at": null, "pinned_at": null,
"skip_push": false,
"pin_expires": null, "pin_expires": null,
"pinned_by": null "pinned_by": null
}, },
@@ -1297,7 +1273,6 @@ void main() {
"silent": false, "silent": false,
"pinned": false, "pinned": false,
"pinned_at": null, "pinned_at": null,
"skip_push": false,
"pin_expires": null, "pin_expires": null,
"pinned_by": null "pinned_by": null
}, },
@@ -1314,7 +1289,6 @@ void main() {
"silent": false, "silent": false,
"pinned": false, "pinned": false,
"pinned_at": null, "pinned_at": null,
"skip_push": false,
"pin_expires": null, "pin_expires": null,
"pinned_by": null "pinned_by": null
} }
@@ -133,7 +133,6 @@ void main() {
"id": "4637f7e4-a06b-42db-ba5a-8d8270dd926f", "id": "4637f7e4-a06b-42db-ba5a-8d8270dd926f",
"text": "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA", "text": "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA",
"silent": false, "silent": false,
"skip_push": false,
"attachments": [ "attachments": [
{ {
"type": "video", "type": "video",
@@ -12,6 +12,9 @@ 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?);
/// Callback called when tapping on a channel
typedef ChannelInfoCallback = void Function(Channel);
/// Builder used to create a custom [ChannelPreview] from a [Channel] /// Builder used to create a custom [ChannelPreview] from a [Channel]
typedef ChannelPreviewBuilder = Widget Function(BuildContext, Channel); typedef ChannelPreviewBuilder = Widget Function(BuildContext, Channel);
@@ -78,6 +81,9 @@ class ChannelListView extends StatefulWidget {
this.emptyBuilder, this.emptyBuilder,
this.loadingBuilder, this.loadingBuilder,
this.listBuilder, this.listBuilder,
this.onMoreDetailsPressed,
this.onDeletePressed,
this.swipeActions,
}) : super(key: key); }) : super(key: key);
/// If true a default swipe to action behaviour will be added to this widget /// If true a default swipe to action behaviour will be added to this widget
@@ -158,6 +164,15 @@ class ChannelListView extends StatefulWidget {
/// The builder used when the channel list is empty. /// The builder used when the channel list is empty.
final WidgetBuilder? emptyBuilder; final WidgetBuilder? emptyBuilder;
/// Callback used when the more details slidable option is pressed
final ChannelInfoCallback? onMoreDetailsPressed;
/// Callback used when the delete slidable option is pressed
final ChannelInfoCallback? onDeletePressed;
/// List of actions for slidable
final List<SwipeAction>? swipeActions;
@override @override
_ChannelListViewState createState() => _ChannelListViewState(); _ChannelListViewState createState() => _ChannelListViewState();
} }
@@ -465,61 +480,79 @@ class _ChannelListViewState extends State<ChannelListView> {
enabled: widget.swipeToAction, enabled: widget.swipeToAction,
actionPane: const SlidableBehindActionPane(), actionPane: const SlidableBehindActionPane(),
actionExtentRatio: 0.12, actionExtentRatio: 0.12,
secondaryActions: <Widget>[ secondaryActions: widget.swipeActions
IconSlideAction( ?.map((e) => IconSlideAction(
color: backgroundColor, color: e.color,
icon: Icons.more_horiz, iconWidget: e.iconWidget,
onTap: () { onTap: () {
showModalBottomSheet( e.onTap?.call(channel);
clipBehavior: Clip.hardEdge, },
shape: const RoundedRectangleBorder( ))
borderRadius: BorderRadius.only( .toList() ??
topLeft: Radius.circular(32), <Widget>[
topRight: Radius.circular(32), IconSlideAction(
), color: backgroundColor,
), icon: Icons.more_horiz,
context: context, onTap: widget.onMoreDetailsPressed != null
builder: (context) => StreamChannel( ? () {
channel: channel, widget.onMoreDetailsPressed!(channel);
child: ChannelBottomSheet( }
onViewInfoTap: () { : () {
widget.onViewInfoTap?.call(channel); showModalBottomSheet(
}, clipBehavior: Clip.hardEdge,
), shape: const RoundedRectangleBorder(
), borderRadius: BorderRadius.only(
); topLeft: Radius.circular(32),
}, topRight: Radius.circular(32),
), ),
if ([ ),
'admin', context: context,
'owner', builder: (context) => StreamChannel(
].contains(channel.state!.members channel: channel,
.firstWhereOrNull( child: ChannelBottomSheet(
(m) => m.userId == channel.client.state.user?.id) onViewInfoTap: () {
?.role)) widget.onViewInfoTap?.call(channel);
IconSlideAction( },
color: backgroundColor, ),
iconWidget: StreamSvgIcon.delete( ),
color: chatThemeData.colorTheme.accentRed, );
},
), ),
onTap: () async { if ([
final res = await showConfirmationDialog( 'admin',
context, 'owner',
title: 'Delete Conversation', ].contains(channel.state!.members
okText: 'DELETE', .firstWhereOrNull(
question: (m) => m.userId == channel.client.state.user?.id)
'Are you sure you want to delete this conversation?', ?.role))
cancelText: 'CANCEL', IconSlideAction(
icon: StreamSvgIcon.delete( color: backgroundColor,
iconWidget: StreamSvgIcon.delete(
color: chatThemeData.colorTheme.accentRed, color: chatThemeData.colorTheme.accentRed,
), ),
); onTap: widget.onDeletePressed != null
if (res == true) { ? () {
await channel.delete(); widget.onDeletePressed!(channel);
} }
}, : () async {
), final res = await showConfirmationDialog(
], context,
title: 'Delete Conversation',
okText: 'DELETE',
question:
// ignore: lines_longer_than_80_chars
'Are you sure you want to delete this conversation?',
cancelText: 'CANCEL',
icon: StreamSvgIcon.delete(
color: chatThemeData.colorTheme.accentRed,
),
);
if (res == true) {
await channel.delete();
}
},
),
],
child: Container( child: Container(
color: chatThemeData.colorTheme.whiteSnow, color: chatThemeData.colorTheme.whiteSnow,
child: widget.channelPreviewBuilder?.call(context, channel) ?? child: widget.channelPreviewBuilder?.call(context, channel) ??
@@ -640,3 +673,22 @@ class _ChannelListViewState extends State<ChannelListView> {
); );
} }
} }
/// Class for slidable action
class SwipeAction {
/// Constructor for creating [SwipeAction]
SwipeAction({
this.color,
required this.iconWidget,
this.onTap,
});
/// Background color of action
Color? color;
/// Widget to display as icon
Widget iconWidget;
/// Callback when icon is tapped
ChannelInfoCallback? onTap;
}
@@ -37,6 +37,7 @@ class MessageActionsModal extends StatefulWidget {
this.customActions = const [], this.customActions = const [],
this.attachmentBorderRadiusGeometry, this.attachmentBorderRadiusGeometry,
this.onCopyTap, this.onCopyTap,
this.textBuilder,
}) : super(key: key); }) : super(key: key);
/// Builder for edit message /// Builder for edit message
@@ -105,6 +106,9 @@ class MessageActionsModal extends StatefulWidget {
/// List of custom actions /// List of custom actions
final List<MessageAction> customActions; final List<MessageAction> customActions;
/// Customize the MessageWidget textBuilder
final Widget Function(BuildContext context, Message message)? textBuilder;
@override @override
_MessageActionsModalState createState() => _MessageActionsModalState(); _MessageActionsModalState createState() => _MessageActionsModalState();
} }
@@ -231,6 +235,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
shape: widget.messageShape, shape: widget.messageShape,
attachmentShape: widget.attachmentShape, attachmentShape: widget.attachmentShape,
showPinHighlight: false, showPinHighlight: false,
textBuilder: widget.textBuilder,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
@@ -156,9 +156,10 @@ class MessageListView extends StatefulWidget {
this.onMessageTap, this.onMessageTap,
this.onSystemMessageTap, this.onSystemMessageTap,
this.onAttachmentTap, this.onAttachmentTap,
this.textBuilder,
this.onLinkTap, this.onLinkTap,
this.pinPermissions = const [], this.pinPermissions = const [],
this.textBuilder,
this.usernameBuilder,
}) : super(key: key); }) : super(key: key);
/// Function used to build a custom message widget /// Function used to build a custom message widget
@@ -260,7 +261,10 @@ class MessageListView extends StatefulWidget {
final void Function(Message message, Attachment attachment)? onAttachmentTap; final void Function(Message message, Attachment attachment)? onAttachmentTap;
/// Customize the MessageWidget textBuilder /// 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 /// Callback for when link is tapped
final void Function(String link)? onLinkTap; final void Function(String link)? onLinkTap;
@@ -865,8 +869,8 @@ class _MessageListViewState extends State<MessageListView> {
} }
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
}, },
textBuilder: textBuilder: widget.textBuilder,
widget.textBuilder as Widget Function(BuildContext, Message)?, usernameBuilder: widget.usernameBuilder,
onLinkTap: widget.onLinkTap, onLinkTap: widget.onLinkTap,
showPinButton: widget.pinPermissions.contains(currentUserMember.role), showPinButton: widget.pinPermissions.contains(currentUserMember.role),
); );
@@ -1070,8 +1074,8 @@ class _MessageListViewState extends State<MessageListView> {
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
}, },
onAttachmentTap: widget.onAttachmentTap, onAttachmentTap: widget.onAttachmentTap,
textBuilder: textBuilder: widget.textBuilder,
widget.textBuilder as Widget Function(BuildContext, Message)?, usernameBuilder: widget.usernameBuilder,
onLinkTap: widget.onLinkTap, onLinkTap: widget.onLinkTap,
showPinButton: widget.pinPermissions.contains(currentUserMember.role), showPinButton: widget.pinPermissions.contains(currentUserMember.role),
); );
@@ -23,6 +23,7 @@ class MessageReactionsModal extends StatelessWidget {
this.showUserAvatar = DisplayWidget.show, this.showUserAvatar = DisplayWidget.show,
this.onUserAvatarTap, this.onUserAvatarTap,
this.attachmentBorderRadiusGeometry, this.attachmentBorderRadiusGeometry,
this.textBuilder,
}) : super(key: key); }) : super(key: key);
/// Message to display reactions of /// Message to display reactions of
@@ -52,6 +53,9 @@ class MessageReactionsModal extends StatelessWidget {
/// [BorderRadius] to apply to attachments /// [BorderRadius] to apply to attachments
final BorderRadius? attachmentBorderRadiusGeometry; final BorderRadius? attachmentBorderRadiusGeometry;
/// Customize the MessageWidget textBuilder
final Widget Function(BuildContext context, Message message)? textBuilder;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final size = MediaQuery.of(context).size; final size = MediaQuery.of(context).size;
@@ -157,6 +161,7 @@ class MessageReactionsModal extends StatelessWidget {
), ),
showReactionPickerIndicator: showReactions && showReactionPickerIndicator: showReactions &&
(message.status == MessageSendingStatus.sent), (message.status == MessageSendingStatus.sent),
textBuilder: textBuilder,
showPinHighlight: false, showPinHighlight: false,
), ),
), ),
@@ -104,6 +104,7 @@ class MessageWidget extends StatefulWidget {
this.onQuotedMessageTap, this.onQuotedMessageTap,
this.customActions = const [], this.customActions = const [],
this.onAttachmentTap, this.onAttachmentTap,
this.usernameBuilder,
}) : attachmentBuilders = { }) : attachmentBuilders = {
'image': (context, message, attachments) { 'image': (context, message, attachments) {
final border = RoundedRectangleBorder( final border = RoundedRectangleBorder(
@@ -267,6 +268,9 @@ class MessageWidget extends StatefulWidget {
/// Widget builder for building text /// Widget builder for building text
final Widget Function(BuildContext, Message)? textBuilder; final Widget Function(BuildContext, Message)? textBuilder;
/// Widget builder for building username
final Widget Function(BuildContext, Message)? usernameBuilder;
/// Function called on long press /// Function called on long press
final void Function(BuildContext, Message)? onMessageActions; final void Function(BuildContext, Message)? onMessageActions;
@@ -751,14 +755,7 @@ class _MessageWidgetState extends State<MessageWidget>
child: Text(msg, style: widget.messageTheme.replies), child: Text(msg, style: widget.messageTheme.replies),
), ),
], ],
if (showUsername) if (showUsername) _buildUsername(usernameKey),
Text(
widget.message.user!.name,
maxLines: 1,
key: usernameKey,
style: widget.messageTheme.messageAuthor,
overflow: TextOverflow.ellipsis,
),
if (showTimeStamp) if (showTimeStamp)
Text( Text(
Jiffy(widget.message.createdAt.toLocal()).jm, Jiffy(widget.message.createdAt.toLocal()).jm,
@@ -821,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() { Widget _buildUrlAttachment() {
final urlAttachment = widget.message.attachments final urlAttachment = widget.message.attachments
.firstWhere((element) => element.ogScrapeUrl != null); .firstWhere((element) => element.ogScrapeUrl != null);
@@ -912,6 +922,7 @@ class _MessageWidgetState extends State<MessageWidget>
builder: (context) => StreamChannel( builder: (context) => StreamChannel(
channel: channel, channel: channel,
child: MessageActionsModal( child: MessageActionsModal(
textBuilder: widget.textBuilder,
onCopyTap: (message) => onCopyTap: (message) =>
Clipboard.setData(ClipboardData(text: message.text)), Clipboard.setData(ClipboardData(text: message.text)),
attachmentBorderRadiusGeometry: attachmentBorderRadiusGeometry:
@@ -962,6 +973,7 @@ class _MessageWidgetState extends State<MessageWidget>
builder: (context) => StreamChannel( builder: (context) => StreamChannel(
channel: channel, channel: channel,
child: MessageReactionsModal( child: MessageReactionsModal(
textBuilder: widget.textBuilder,
attachmentBorderRadiusGeometry: attachmentBorderRadiusGeometry:
widget.attachmentBorderRadiusGeometry as BorderRadius?, widget.attachmentBorderRadiusGeometry as BorderRadius?,
showUserAvatar: showUserAvatar: