migrate ui

This commit is contained in:
Salvatore Giordano
2021-04-30 14:58:50 +02:00
parent 2c02f0f631
commit 34888d0fff
103 changed files with 2622 additions and 2455 deletions
@@ -26,7 +26,7 @@ typedef AttachmentBuilder = Widget Function(
Message,
List<Attachment>,
);
typedef OnQuotedMessageTap = void Function(String);
typedef OnQuotedMessageTap = void Function(String?);
/// The display behaviour of a widget
enum DisplayWidget {
@@ -51,46 +51,46 @@ enum DisplayWidget {
/// Modify it to change the widget appearance.
class MessageWidget extends StatefulWidget {
/// Function called on mention tap
final void Function(User) onMentionTap;
final void Function(User)? onMentionTap;
/// The function called when tapping on replies
final void Function(Message) onThreadTap;
final void Function(Message) onReplyTap;
final Widget Function(BuildContext, Message) editMessageInputBuilder;
final Widget Function(BuildContext, Message) textBuilder;
final void Function(Message)? onThreadTap;
final void Function(Message)? onReplyTap;
final Widget Function(BuildContext, Message)? editMessageInputBuilder;
final Widget Function(BuildContext, Message)? textBuilder;
/// Function called on long press
final void Function(BuildContext, Message) onMessageActions;
final void Function(BuildContext, Message)? onMessageActions;
/// The message
final Message message;
/// The message theme
final MessageTheme messageTheme;
final MessageTheme? messageTheme;
/// If true the widget will be mirrored
final bool reverse;
/// The shape of the message text
final ShapeBorder shape;
final ShapeBorder? shape;
/// The shape of an attachment
final ShapeBorder attachmentShape;
final ShapeBorder? attachmentShape;
/// The borderside of the message text
final BorderSide borderSide;
final BorderSide? borderSide;
/// The borderside of an attachment
final BorderSide attachmentBorderSide;
final BorderSide? attachmentBorderSide;
/// The border radius of the message text
final BorderRadiusGeometry borderRadiusGeometry;
final BorderRadiusGeometry? borderRadiusGeometry;
/// The border radius of an attachment
final BorderRadiusGeometry attachmentBorderRadiusGeometry;
final BorderRadiusGeometry? attachmentBorderRadiusGeometry;
/// The padding of the widget
final EdgeInsetsGeometry padding;
final EdgeInsetsGeometry? padding;
/// The internal padding of the message text
final EdgeInsetsGeometry textPadding;
@@ -116,18 +116,18 @@ class MessageWidget extends StatefulWidget {
final bool showInChannelIndicator;
/// The function called when tapping on UserAvatar
final void Function(User) onUserAvatarTap;
final void Function(User)? onUserAvatarTap;
/// The function called when tapping on a link
final void Function(String) onLinkTap;
final void Function(String)? onLinkTap;
/// Used in [MessageReactionsModal] and [MessageActionsModal]
final bool showReactionPickerIndicator;
final List<Read> readList;
final List<Read>? readList;
final ShowMessageCallback onShowMessage;
final ValueChanged<ReturnActionType> onReturnAction;
final ShowMessageCallback? onShowMessage;
final ValueChanged<ReturnActionType>? onReturnAction;
/// If true show the users username next to the timestamp of the message
final bool showUsername;
@@ -147,22 +147,22 @@ class MessageWidget extends StatefulWidget {
final bool translateUserAvatar;
/// Function called when quotedMessage is tapped
final OnQuotedMessageTap onQuotedMessageTap;
final OnQuotedMessageTap? onQuotedMessageTap;
/// Function called when message is tapped
final void Function(Message) onMessageTap;
final void Function(Message)? onMessageTap;
/// List of custom actions shown on message long tap
final List<MessageAction> customActions;
// Customize onTap on attachment
final void Function(Message message, Attachment attachment) onAttachmentTap;
final void Function(Message message, Attachment attachment)? onAttachmentTap;
///
MessageWidget({
Key key,
@required this.message,
@required this.messageTheme,
Key? key,
required this.message,
required this.messageTheme,
this.reverse = false,
this.translateUserAvatar = true,
this.shape,
@@ -197,7 +197,7 @@ class MessageWidget extends StatefulWidget {
this.editMessageInputBuilder,
this.textBuilder,
this.onReturnAction,
Map<String, AttachmentBuilder> customAttachmentBuilders,
Map<String, AttachmentBuilder>? customAttachmentBuilders,
this.readList,
this.padding,
this.textPadding = const EdgeInsets.symmetric(
@@ -222,7 +222,7 @@ class MessageWidget extends StatefulWidget {
child: wrapAttachmentWidget(
context,
Material(
color: messageTheme.messageBackgroundColor,
color: messageTheme?.messageBackgroundColor,
child: ImageGroup(
size: Size(
MediaQuery.of(context).size.width * 0.8,
@@ -236,7 +236,8 @@ class MessageWidget extends StatefulWidget {
),
border,
reverse,
attachmentBorderRadiusGeometry ?? BorderRadius.zero,
attachmentBorderRadiusGeometry as BorderRadius? ??
BorderRadius.zero,
),
);
}
@@ -255,13 +256,14 @@ class MessageWidget extends StatefulWidget {
onReturnAction: onReturnAction,
onAttachmentTap: onAttachmentTap != null
? () {
onAttachmentTap?.call(message, attachments[0]);
onAttachmentTap.call(message, attachments[0]);
}
: null,
),
border,
reverse,
attachmentBorderRadiusGeometry ?? BorderRadius.zero,
attachmentBorderRadiusGeometry as BorderRadius? ??
BorderRadius.zero,
);
},
'video': (context, message, attachments) {
@@ -286,7 +288,7 @@ class MessageWidget extends StatefulWidget {
onReturnAction: onReturnAction,
onAttachmentTap: onAttachmentTap != null
? () {
onAttachmentTap?.call(message, attachment);
onAttachmentTap(message, attachment);
}
: null,
);
@@ -294,11 +296,12 @@ class MessageWidget extends StatefulWidget {
),
border,
reverse,
attachmentBorderRadiusGeometry ?? BorderRadius.zero,
attachmentBorderRadiusGeometry as BorderRadius? ??
BorderRadius.zero,
);
},
'giphy': (context, message, attachments) {
var border = RoundedRectangleBorder(
final border = RoundedRectangleBorder(
side: BorderSide.none,
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
);
@@ -309,7 +312,6 @@ class MessageWidget extends StatefulWidget {
children: attachments.map((attachment) {
return GiphyAttachment(
attachment: attachment,
messageTheme: messageTheme,
message: message,
size: Size(
MediaQuery.of(context).size.width * 0.8,
@@ -322,7 +324,8 @@ class MessageWidget extends StatefulWidget {
),
border,
reverse,
attachmentBorderRadiusGeometry ?? BorderRadius.zero,
attachmentBorderRadiusGeometry as BorderRadius? ??
BorderRadius.zero,
);
},
'file': (context, message, attachments) {
@@ -349,7 +352,8 @@ class MessageWidget extends StatefulWidget {
),
border,
reverse,
attachmentBorderRadiusGeometry ?? BorderRadius.zero,
attachmentBorderRadiusGeometry as BorderRadius? ??
BorderRadius.zero,
);
})
.insertBetween(SizedBox(
@@ -381,7 +385,7 @@ class _MessageWidgetState extends State<MessageWidget>
bool get showInChannel => widget.showInChannelIndicator;
bool get hasQuotedMessage => widget.message?.quotedMessage != null;
bool get hasQuotedMessage => widget.message.quotedMessage != null;
bool get isSendFailed => widget.message.status == MessageSendingStatus.failed;
@@ -394,17 +398,17 @@ class _MessageWidgetState extends State<MessageWidget>
bool get isFailedState => isSendFailed || isUpdateFailed || isDeleteFailed;
bool get isGiphy =>
widget.message.attachments?.any((element) => element.type == 'giphy') ==
widget.message.attachments.any((element) => element.type == 'giphy') ==
true;
bool get hasNonUrlAttachments =>
widget.message.attachments
?.where((it) => it.ogScrapeUrl == null)
?.isNotEmpty ==
.where((it) => it.ogScrapeUrl == null)
.isNotEmpty ==
true;
bool get hasUrlAttachments =>
widget.message.attachments?.any((it) => it.ogScrapeUrl != null) == true;
widget.message.attachments.any((it) => it.ogScrapeUrl != null) == true;
bool get showBottomRow =>
showThreadReplyIndicator ||
@@ -415,12 +419,13 @@ class _MessageWidgetState extends State<MessageWidget>
isDeleted;
@override
bool get wantKeepAlive => widget.message.attachments?.isNotEmpty == true;
bool get wantKeepAlive => widget.message.attachments.isNotEmpty == true;
@override
Widget build(BuildContext context) {
super.build(context);
final avatarWidth = widget.messageTheme.avatarTheme.constraints.maxWidth;
final avatarWidth =
widget.messageTheme?.avatarTheme?.constraints.maxWidth ?? 40;
var leftPadding =
widget.showUserAvatar != DisplayWidget.gone ? avatarWidth + 8.5 : 0.5;
@@ -429,7 +434,7 @@ class _MessageWidgetState extends State<MessageWidget>
child: Portal(
child: InkWell(
onTap: () {
widget.onMessageTap(widget.message);
widget.onMessageTap!(widget.message);
},
onLongPress: widget.message.isDeleted && !isFailedState
? null
@@ -460,7 +465,8 @@ class _MessageWidgetState extends State<MessageWidget>
mainAxisSize: MainAxisSize.min,
children: <Widget>[
if (widget.showUserAvatar ==
DisplayWidget.show) ...[
DisplayWidget.show &&
widget.message.user != null) ...[
_buildUserAvatar(),
SizedBox(width: 4),
],
@@ -533,13 +539,14 @@ class _MessageWidgetState extends State<MessageWidget>
),
shape: widget.shape ??
RoundedRectangleBorder(
side:
widget.borderSide ??
BorderSide(
color: widget
side: widget
.borderSide ??
BorderSide(
color: widget
.messageTheme
.messageBorderColor,
),
?.messageBorderColor ??
Colors.grey,
),
borderRadius: widget
.borderRadiusGeometry ??
BorderRadius.zero,
@@ -616,14 +623,14 @@ 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.message.user?.id == StreamChat.of(context).user?.id;
final onTap = widget.message.quotedMessage?.isDeleted != true &&
widget.onQuotedMessageTap != null
? () => widget.onQuotedMessageTap(widget.message.quotedMessageId)
? () => widget.onQuotedMessageTap!(widget.message.quotedMessageId)
: null;
return QuotedMessageWidget(
onTap: onTap,
message: widget.message.quotedMessage,
message: widget.message.quotedMessage!,
messageTheme: isMyMessage
? StreamChatTheme.of(context).otherMessageTheme
: StreamChatTheme.of(context).ownMessageTheme,
@@ -660,12 +667,12 @@ class _MessageWidgetState extends State<MessageWidget>
var children = <Widget>[];
final threadParticipants = widget.message?.threadParticipants?.take(2);
final threadParticipants = widget.message.threadParticipants?.take(2);
final showThreadParticipants = threadParticipants?.isNotEmpty == true;
final replyCount = widget.message.replyCount;
var msg = 'Thread Reply';
if (showThreadReplyIndicator && replyCount > 1) {
if (showThreadReplyIndicator && replyCount! > 1) {
msg = '$replyCount Thread Replies';
}
@@ -674,9 +681,9 @@ class _MessageWidgetState extends State<MessageWidget>
var message = widget.message;
if (showInChannel) {
final channel = StreamChannel.of(context);
message = await channel.getMessage(widget.message.parentId);
message = await channel.getMessage(widget.message.parentId!);
}
return widget.onThreadTap(message);
return widget.onThreadTap!(message);
} catch (e, stk) {
print(e);
print(stk);
@@ -690,7 +697,7 @@ class _MessageWidgetState extends State<MessageWidget>
if (showInChannel || showThreadReplyIndicator) ...[
if (showThreadParticipants)
SizedBox.fromSize(
size: Size((threadParticipants.length * 8.0) + 8, 16),
size: Size((threadParticipants!.length * 8.0) + 8, 16),
child: _buildThreadParticipantsIndicator(threadParticipants),
),
InkWell(
@@ -700,16 +707,16 @@ class _MessageWidgetState extends State<MessageWidget>
],
if (showUsername)
Text(
widget.message.user.name,
widget.message.user!.name,
maxLines: 1,
key: usernameKey,
style: widget.messageTheme.messageAuthor,
style: widget.messageTheme?.messageAuthor,
overflow: TextOverflow.ellipsis,
),
if (showTimeStamp)
Text(
Jiffy(widget.message.createdAt.toLocal()).jm,
style: widget.messageTheme.createdAt,
style: widget.messageTheme?.createdAt,
),
if (showSendingIndicator) _buildSendingIndicator(),
]);
@@ -724,13 +731,13 @@ class _MessageWidgetState extends State<MessageWidget>
Container(
margin: EdgeInsets.only(
bottom: context.textScaleFactor *
(widget.messageTheme.replies.fontSize / 2),
((widget.messageTheme?.replies?.fontSize ?? 1) / 2),
),
child: CustomPaint(
size: Size(16, 32) * context.textScaleFactor,
painter: _ThreadReplyPainter(
context: context,
color: widget.messageTheme.messageBorderColor,
color: widget.messageTheme?.messageBorderColor,
),
),
),
@@ -758,7 +765,7 @@ class _MessageWidgetState extends State<MessageWidget>
var urlAttachment = widget.message.attachments
.firstWhere((element) => element.ogScrapeUrl != null);
var host = Uri.parse(urlAttachment.ogScrapeUrl).host;
var host = Uri.parse(urlAttachment.ogScrapeUrl!).host;
var splitList = host.split('.');
var hostName = splitList.length == 3 ? splitList[1] : splitList[0];
var hostDisplayName = urlAttachment.authorName?.capitalize() ??
@@ -768,7 +775,7 @@ class _MessageWidgetState extends State<MessageWidget>
return UrlAttachment(
urlAttachment: urlAttachment,
hostDisplayName: hostDisplayName,
textPadding: widget.textPadding,
textPadding: widget.textPadding as EdgeInsets,
);
}
@@ -801,15 +808,16 @@ class _MessageWidgetState extends State<MessageWidget>
Widget _buildReactionIndicator(
BuildContext context,
) {
final ownId = StreamChat.of(context).user.id;
final ownId = StreamChat.of(context).user!.id;
final reactionsMap = <String, Reaction>{};
widget.message.latestReactions?.forEach((element) {
if (!reactionsMap.containsKey(element.type) || element.user.id == ownId) {
if (!reactionsMap.containsKey(element.type) ||
element.user!.id == ownId) {
reactionsMap[element.type] = element;
}
});
final reactionsList = reactionsMap.values.toList()
..sort((a, b) => a.user.id == ownId ? 1 : -1);
..sort((a, b) => a.user!.id == ownId ? 1 : -1);
return AnimatedSwitcher(
duration: Duration(milliseconds: 300),
@@ -822,9 +830,13 @@ class _MessageWidgetState extends State<MessageWidget>
key: ValueKey('${widget.message.id}.reactions'),
reverse: widget.reverse,
flipTail: widget.reverse,
backgroundColor: widget.messageTheme.reactionsBackgroundColor,
borderColor: widget.messageTheme.reactionsBorderColor,
maskColor: widget.messageTheme.reactionsMaskColor,
backgroundColor:
widget.messageTheme?.reactionsBackgroundColor ??
Colors.transparent,
borderColor: widget.messageTheme?.reactionsBorderColor ??
Colors.transparent,
maskColor: widget.messageTheme?.reactionsMaskColor ??
Colors.transparent,
reactions: reactionsList,
),
)
@@ -845,9 +857,9 @@ class _MessageWidgetState extends State<MessageWidget>
onCopyTap: (message) =>
Clipboard.setData(ClipboardData(text: message.text)),
attachmentBorderRadiusGeometry:
widget.attachmentBorderRadiusGeometry,
widget.attachmentBorderRadiusGeometry as BorderRadius?,
showUserAvatar:
widget.message.user.id == channel.client.state.user.id
widget.message.user!.id == channel.client.state.user!.id
? DisplayWidget.gone
: DisplayWidget.show,
messageTheme: widget.messageTheme,
@@ -864,11 +876,11 @@ class _MessageWidgetState extends State<MessageWidget>
widget.showResendMessage && (isSendFailed || isUpdateFailed),
showCopyMessage: widget.showCopyMessage &&
!isFailedState &&
widget.message.text?.trim()?.isNotEmpty == true,
widget.message.text?.trim().isNotEmpty == true,
showEditMessage: widget.showEditMessage &&
!isDeleteFailed &&
widget.message.attachments
?.any((element) => element.type == 'giphy') !=
.any((element) => element.type == 'giphy') !=
true,
showReactions: widget.showReactions,
showReplyMessage: widget.showReplyMessage &&
@@ -894,9 +906,9 @@ class _MessageWidgetState extends State<MessageWidget>
channel: channel,
child: MessageReactionsModal(
attachmentBorderRadiusGeometry:
widget.attachmentBorderRadiusGeometry,
widget.attachmentBorderRadiusGeometry as BorderRadius?,
showUserAvatar:
widget.message.user.id == channel.client.state.user.id
widget.message.user!.id == channel.client.state.user!.id
? DisplayWidget.gone
: DisplayWidget.show,
onUserAvatarTap: widget.onUserAvatarTap,
@@ -914,7 +926,7 @@ class _MessageWidgetState extends State<MessageWidget>
ShapeBorder _getDefaultAttachmentShape(BuildContext context) {
final hasFiles =
widget.message.attachments?.any((it) => it.type == 'file') == true;
widget.message.attachments.any((it) => it.type == 'file') == true;
return RoundedRectangleBorder(
side: hasFiles
? widget.attachmentBorderSide ??
@@ -940,13 +952,13 @@ class _MessageWidgetState extends State<MessageWidget>
final attachmentGroups = <String, List<Attachment>>{};
widget.message.attachments
.where((element) => element.ogScrapeUrl == null)
.where((element) => element.ogScrapeUrl == null && element.type != null)
.forEach((e) {
if (attachmentGroups[e.type] == null) {
attachmentGroups[e.type] = [];
attachmentGroups[e.type!] = [];
}
attachmentGroups[e.type].add(e);
attachmentGroups[e.type]?.add(e);
});
final attachmentList = <Widget>[];
@@ -954,7 +966,7 @@ class _MessageWidgetState extends State<MessageWidget>
attachmentGroups.forEach((type, attachments) {
final attachmentBuilder = widget.attachmentBuilders[type];
if (attachmentBuilder == null) return SizedBox();
if (attachmentBuilder == null) return;
final attachmentWidget = attachmentBuilder(
context,
widget.message,
@@ -967,10 +979,9 @@ class _MessageWidgetState extends State<MessageWidget>
padding: widget.attachmentPadding,
child: Column(
mainAxisSize: MainAxisSize.min,
children: attachmentList?.insertBetween(SizedBox(
height: widget.attachmentPadding.vertical / 2,
)) ??
[],
children: attachmentList.insertBetween(SizedBox(
height: widget.attachmentPadding.vertical / 2,
)),
),
);
}
@@ -982,7 +993,7 @@ class _MessageWidgetState extends State<MessageWidget>
}
if (widget.onMessageActions != null) {
widget.onMessageActions(context, widget.message);
widget.onMessageActions!(context, widget.message);
} else {
_showMessageActionModalBottomSheet(context);
}
@@ -990,7 +1001,7 @@ class _MessageWidgetState extends State<MessageWidget>
}
Widget _buildSendingIndicator() {
final style = widget.messageTheme.createdAt;
final style = widget.messageTheme?.createdAt;
final message = widget.message;
if (hasNonUrlAttachments &&
@@ -1002,8 +1013,8 @@ class _MessageWidgetState extends State<MessageWidget>
}).length;
if (uploadRemaining == 0) {
return StreamSvgIcon.check(
size: style.fontSize,
color: IconTheme.of(context).color.withOpacity(0.5),
size: style!.fontSize,
color: IconTheme.of(context).color!.withOpacity(0.5),
);
}
return Text(
@@ -1015,14 +1026,14 @@ class _MessageWidgetState extends State<MessageWidget>
Widget child = SendingIndicator(
message: message,
isMessageRead: isMessageRead,
size: style.fontSize,
size: style!.fontSize,
);
if (isMessageRead) {
child = Row(
children: [
if (StreamChannel.of(context).channel.memberCount > 2)
if (StreamChannel.of(context).channel.memberCount! > 2)
Text(
widget.readList.length.toString(),
widget.readList!.length.toString(),
style: style.copyWith(
color: StreamChatTheme.of(context).colorTheme.accentBlue,
),
@@ -1042,21 +1053,23 @@ class _MessageWidgetState extends State<MessageWidget>
offset: Offset(
0,
widget.translateUserAvatar
? widget.messageTheme.avatarTheme.constraints.maxHeight / 2
? (widget.messageTheme?.avatarTheme?.constraints.maxHeight ??
40) /
2
: 0,
),
child: UserAvatar(
user: widget.message.user,
user: widget.message.user!,
onTap: widget.onUserAvatarTap,
constraints: widget.messageTheme.avatarTheme.constraints,
borderRadius: widget.messageTheme.avatarTheme.borderRadius,
constraints: widget.messageTheme?.avatarTheme!.constraints,
borderRadius: widget.messageTheme?.avatarTheme!.borderRadius,
showOnlineStatus: false,
),
),
);
Widget _buildTextBubble() {
if (widget.message.text.trim().isEmpty) return Offstage();
if (widget.message.text!.trim().isEmpty) return Offstage();
return Transform(
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
alignment: Alignment.center,
@@ -1066,15 +1079,15 @@ class _MessageWidgetState extends State<MessageWidget>
Padding(
padding: isOnlyEmoji ? EdgeInsets.zero : widget.textPadding,
child: widget.textBuilder != null
? widget.textBuilder(context, widget.message)
? widget.textBuilder!(context, widget.message)
: MessageText(
onLinkTap: widget.onLinkTap,
message: widget.message,
onMentionTap: widget.onMentionTap,
messageTheme: isOnlyEmoji
? widget.messageTheme.copyWith(
? widget.messageTheme?.copyWith(
messageText:
widget.messageTheme.messageText.copyWith(
widget.messageTheme?.messageText!.copyWith(
fontSize: 42,
))
: widget.messageTheme,
@@ -1086,11 +1099,11 @@ class _MessageWidgetState extends State<MessageWidget>
);
}
bool get isOnlyEmoji => widget.message.text.isOnlyEmoji;
bool get isOnlyEmoji => widget.message.text!.isOnlyEmoji;
Color _getBackgroundColor() {
Color? _getBackgroundColor() {
if (hasQuotedMessage) {
return widget.messageTheme.messageBackgroundColor;
return widget.messageTheme?.messageBackgroundColor;
}
if (hasUrlAttachments) {
@@ -1105,7 +1118,7 @@ class _MessageWidgetState extends State<MessageWidget>
return Colors.transparent;
}
return widget.messageTheme.messageBackgroundColor;
return widget.messageTheme?.messageBackgroundColor;
}
void retryMessage(BuildContext context) {
@@ -1127,15 +1140,15 @@ class _MessageWidgetState extends State<MessageWidget>
}
class _ThreadReplyPainter extends CustomPainter {
final Color color;
final BuildContext context;
final Color? color;
final BuildContext? context;
const _ThreadReplyPainter({this.context, @required this.color});
const _ThreadReplyPainter({this.context, required this.color});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color ?? StreamChatTheme.of(context).colorTheme.greyGainsboro
..color = color ?? StreamChatTheme.of(context!).colorTheme.greyGainsboro
..style = PaintingStyle.stroke
..strokeWidth = 1
..strokeCap = StrokeCap.round;