From bc9d53624f3b930e409daa36acf61fc1d1f3a878 Mon Sep 17 00:00:00 2001 From: RaulRodrigo06 Date: Fri, 20 Oct 2023 15:16:14 -0300 Subject: [PATCH 01/22] feat: adding reply container --- .../controllers/chat/ds_reply.controller.dart | 15 +++ lib/src/themes/colors/ds_colors.theme.dart | 2 + .../audio/ds_audio_message_bubble.widget.dart | 3 + .../ds_application_json_bubble.widget.dart | 4 + .../ds_contact_message_bubble.widget.dart | 3 + .../chat/ds_file_message_bubble.widget.dart | 3 + .../chat/ds_image_message_bubble.widget.dart | 3 + .../ds_location_message_bubble.widget.dart | 3 + .../chat/ds_message_bubble.widget.dart | 17 ++- .../chat/ds_reply_container.widget.dart | 116 ++++++++++++++++++ .../ds_request_location_bubble.widget.dart | 3 + .../chat/ds_text_message_bubble.widget.dart | 3 + ...pported_content_message_bubble.widget.dart | 3 + lib/src/widgets/chat/ds_weblink.widget.dart | 5 + .../video/ds_video_message_bubble.widget.dart | 5 + lib/src/widgets/utils/ds_card.widget.dart | 16 +++ .../widgets/utils/ds_group_card.widget.dart | 8 ++ 17 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 lib/src/controllers/chat/ds_reply.controller.dart create mode 100644 lib/src/widgets/chat/ds_reply_container.widget.dart diff --git a/lib/src/controllers/chat/ds_reply.controller.dart b/lib/src/controllers/chat/ds_reply.controller.dart new file mode 100644 index 00000000..0586f3fb --- /dev/null +++ b/lib/src/controllers/chat/ds_reply.controller.dart @@ -0,0 +1,15 @@ +import 'package:get/get.dart'; + +import '../../models/ds_message_item.model.dart'; + +class DSReplyController extends GetxController { + final List messages = []; + + void addMessages(List newMessages) { + messages.addAll(newMessages); + } + + DSMessageItemModel? getMessageById(String id) { + return messages.firstWhereOrNull((element) => element.id == id); + } +} diff --git a/lib/src/themes/colors/ds_colors.theme.dart b/lib/src/themes/colors/ds_colors.theme.dart index a8bcf7c2..dbc496c4 100644 --- a/lib/src/themes/colors/ds_colors.theme.dart +++ b/lib/src/themes/colors/ds_colors.theme.dart @@ -51,6 +51,8 @@ abstract class DSColors { static const Color surface1 = Color(0xFFF6F6F6); static const Color surface3 = Color(0xFFC7C7C7); static const Color contentDefault = Color(0xFF454545); + static const Color contentGhost = Color(0xFF949494); + static const Color disabledText = Color(0xFF637798); static const Color disabledBg = Color(0xFFE8F2FF); diff --git a/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart b/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart index e1c955d4..aa01d4b4 100644 --- a/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart +++ b/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart @@ -15,6 +15,7 @@ class DSAudioMessageBubble extends StatelessWidget { final String? uniqueId; final String audioType; final bool shouldAuthenticate; + final String? replyId; DSAudioMessageBubble({ super.key, @@ -22,6 +23,7 @@ class DSAudioMessageBubble extends StatelessWidget { required this.align, required this.audioType, this.uniqueId, + this.replyId, this.borderRadius = const [DSBorderRadius.all], this.shouldAuthenticate = false, final DSMessageBubbleStyle? style, @@ -35,6 +37,7 @@ class DSAudioMessageBubble extends StatelessWidget { return DSMessageBubble( borderRadius: borderRadius, align: align, + replyId: replyId, style: style, padding: const EdgeInsets.only( left: 4.0, diff --git a/lib/src/widgets/chat/ds_application_json_bubble.widget.dart b/lib/src/widgets/chat/ds_application_json_bubble.widget.dart index 2463c596..2ec30d9c 100644 --- a/lib/src/widgets/chat/ds_application_json_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_application_json_bubble.widget.dart @@ -15,12 +15,14 @@ class DSApplicationJsonMessageBubble extends StatelessWidget { required this.borderRadius, required this.content, this.status, + this.replyId, DSMessageBubbleStyle? style, }) : style = style ?? DSMessageBubbleStyle(); final DSAlign align; final List borderRadius; final Map content; + final String? replyId; final DSDeliveryReportStatus? status; final DSMessageBubbleStyle style; @@ -31,6 +33,7 @@ class DSApplicationJsonMessageBubble extends StatelessWidget { opacity: status == DSDeliveryReportStatus.failed ? .3 : 1, child: DSUnsupportedContentMessageBubble( align: align, + replyId: replyId, borderRadius: borderRadius, style: style, overflow: TextOverflow.visible, @@ -50,6 +53,7 @@ class DSApplicationJsonMessageBubble extends StatelessWidget { align: align, borderRadius: borderRadius, style: style, + replyId: replyId, ); } } diff --git a/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart b/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart index 5cdbb064..664ca2d1 100644 --- a/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart @@ -6,6 +6,7 @@ class DSContactMessageBubble extends StatelessWidget { final String? phone; final String? email; final String? address; + final String? replyId; final DSAlign align; final List borderRadius; final DSMessageBubbleStyle style; @@ -17,6 +18,7 @@ class DSContactMessageBubble extends StatelessWidget { required this.address, required this.email, required this.align, + this.replyId, this.borderRadius = const [DSBorderRadius.all], DSMessageBubbleStyle? style, }) : style = style ?? DSMessageBubbleStyle(); @@ -29,6 +31,7 @@ class DSContactMessageBubble extends StatelessWidget { padding: const EdgeInsets.all(16.0), shouldUseDefaultSize: true, style: style, + replyId: replyId, child: _buildContactCard(), ); } diff --git a/lib/src/widgets/chat/ds_file_message_bubble.widget.dart b/lib/src/widgets/chat/ds_file_message_bubble.widget.dart index 5f605fab..21b63e25 100644 --- a/lib/src/widgets/chat/ds_file_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_file_message_bubble.widget.dart @@ -22,6 +22,7 @@ class DSFileMessageBubble extends StatelessWidget { final List borderRadius; final DSMessageBubbleStyle style; final bool shouldAuthenticate; + final String? replyId; /// Creates a Design System's [DSMessageBubble] used on files other than image, audio, or video DSFileMessageBubble({ @@ -30,6 +31,7 @@ class DSFileMessageBubble extends StatelessWidget { required this.url, required this.size, required this.filename, + this.replyId, this.borderRadius = const [DSBorderRadius.all], this.shouldAuthenticate = false, DSMessageBubbleStyle? style, @@ -46,6 +48,7 @@ class DSFileMessageBubble extends StatelessWidget { ), child: DSMessageBubble( borderRadius: borderRadius, + replyId: replyId, padding: EdgeInsets.zero, align: align, style: style, diff --git a/lib/src/widgets/chat/ds_image_message_bubble.widget.dart b/lib/src/widgets/chat/ds_image_message_bubble.widget.dart index 51759838..e266ead7 100644 --- a/lib/src/widgets/chat/ds_image_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_image_message_bubble.widget.dart @@ -29,6 +29,7 @@ class DSImageMessageBubble extends StatefulWidget { this.showSelect = false, this.onSelected, this.onOpenLink, + this.replyId, this.shouldAuthenticate = false, }) : style = style ?? DSMessageBubbleStyle(); @@ -40,6 +41,7 @@ class DSImageMessageBubble extends StatefulWidget { final String? text; final String appBarText; final Uri? appBarPhotoUri; + final String? replyId; final DSMessageBubbleStyle style; final List selectOptions; final bool showSelect; @@ -73,6 +75,7 @@ class _DSImageMessageBubbleState extends State defaultMaxSize: 360.0, shouldUseDefaultSize: true, align: widget.align, + replyId:widget.replyId, borderRadius: widget.borderRadius, padding: EdgeInsets.zero, hasSpacer: widget.hasSpacer, diff --git a/lib/src/widgets/chat/ds_location_message_bubble.widget.dart b/lib/src/widgets/chat/ds_location_message_bubble.widget.dart index a838ca8c..688361b9 100644 --- a/lib/src/widgets/chat/ds_location_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_location_message_bubble.widget.dart @@ -16,6 +16,7 @@ class DSLocationMessageBubble extends StatelessWidget { final DSAlign align; final DSMessageBubbleStyle style; final String? title; + final String? replyId; final String latitude; final String longitude; final List borderRadius; @@ -25,6 +26,7 @@ class DSLocationMessageBubble extends StatelessWidget { required this.align, required this.latitude, required this.longitude, + this.replyId, this.borderRadius = const [DSBorderRadius.all], DSMessageBubbleStyle? style, this.title, @@ -56,6 +58,7 @@ class DSLocationMessageBubble extends StatelessWidget { defaultMaxSize: 240.0, defaultMinSize: 240.0, borderRadius: borderRadius, + replyId: replyId, padding: EdgeInsets.zero, align: align, style: style, diff --git a/lib/src/widgets/chat/ds_message_bubble.widget.dart b/lib/src/widgets/chat/ds_message_bubble.widget.dart index cb1a62c5..bcea3632 100644 --- a/lib/src/widgets/chat/ds_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_message_bubble.widget.dart @@ -1,3 +1,4 @@ +import 'package:blip_ds/src/widgets/chat/ds_reply_container.widget.dart'; import 'package:flutter/material.dart'; import '../../enums/ds_align.enum.dart'; @@ -10,6 +11,7 @@ import '../animations/ds_animated_size.widget.dart'; class DSMessageBubble extends StatelessWidget { final DSAlign align; final Widget child; + final String? replyId; final List borderRadius; final EdgeInsets padding; final bool shouldUseDefaultSize; @@ -22,6 +24,7 @@ class DSMessageBubble extends StatelessWidget { Key? key, required this.align, required this.child, + this.replyId, this.borderRadius = const [DSBorderRadius.all], this.padding = const EdgeInsets.symmetric( vertical: 8.0, @@ -60,7 +63,19 @@ class DSMessageBubble extends StatelessWidget { : null, padding: padding, color: style.bubbleBackgroundColor(align), - child: child, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (replyId != null) + DSReplyContainer( + replyId: replyId!, + style: style, + align: align, + ), + child, + ], + ), ), ), ), diff --git a/lib/src/widgets/chat/ds_reply_container.widget.dart b/lib/src/widgets/chat/ds_reply_container.widget.dart new file mode 100644 index 00000000..a4528871 --- /dev/null +++ b/lib/src/widgets/chat/ds_reply_container.widget.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../controllers/chat/ds_reply.controller.dart'; +import '../../enums/ds_align.enum.dart'; +import '../../models/ds_message_bubble_style.model.dart'; +import '../../themes/colors/ds_colors.theme.dart'; +import '../../themes/icons/ds_icons.dart'; +import '../../utils/ds_message_content_type.util.dart'; +import '../texts/ds_body_text.widget.dart'; + +class DSReplyContainer extends StatelessWidget { + const DSReplyContainer({ + super.key, + required this.replyId, + required this.style, + required this.align, + }); + + final DSAlign align; + final String replyId; + final DSMessageBubbleStyle style; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only( + top: 8.0, + left: 8.0, + right: 8.0, + ), + child: DecoratedBox( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4.0), + border: Border.all( + color: style.isLightBubbleBackground(align) + ? DSColors.contentGhost + : DSColors.contentDisable, + ), + color: style.isLightBubbleBackground(align) + ? DSColors.surface3 + : DSColors.contentDefault, + ), + child: IntrinsicHeight( + child: Row( + children: [ + Container( + decoration: const ShapeDecoration( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(8.0), + bottomLeft: Radius.circular(8.0), + ), + ), + color: DSColors.primary, + ), + width: 4.0, + ), + Flexible( + child: Padding( + padding: const EdgeInsets.only( + top: 8.0, + left: 8.0, + bottom: 8.0, + ), + child: _replyWidget(replyId, style, align), + ), + ), + ], + ), + ), + ), + ); + } +} + +Widget _replyWidget( + String id, + DSMessageBubbleStyle style, + DSAlign align, +) { + final replyController = Get.find(); + final message = replyController.getMessageById(id); + + switch (message?.type) { + case DSMessageContentType.textPlain: + return DSBodyText( + message?.content is String ? message?.content : '**********', + color: style.isLightBubbleBackground(align) + ? DSColors.contentDefault + : DSColors.neutralLightSnow, + ); + default: + return Row( + mainAxisSize: MainAxisSize.max, + children: [ + Icon( + DSIcons.warning_outline, + color: style.isLightBubbleBackground(align) + ? DSColors.contentDefault + : DSColors.neutralLightSnow, + ), + const SizedBox(width: 8), + Flexible( + child: DSBodyText( + 'Falha ao carregar mensagem', + overflow: TextOverflow.visible, + color: style.isLightBubbleBackground(align) + ? DSColors.contentDefault + : DSColors.neutralLightSnow, + ), + ), + ], + ); + } +} diff --git a/lib/src/widgets/chat/ds_request_location_bubble.widget.dart b/lib/src/widgets/chat/ds_request_location_bubble.widget.dart index 511b7923..d8ceba85 100644 --- a/lib/src/widgets/chat/ds_request_location_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_request_location_bubble.widget.dart @@ -14,6 +14,7 @@ class DSRequestLocationBubble extends StatelessWidget { required this.label, required this.value, required this.align, + this.replyId, this.type = DSMessageContentType.textPlain, this.borderRadius = const [DSBorderRadius.all], this.showRequestLocationButton = false, @@ -23,6 +24,7 @@ class DSRequestLocationBubble extends StatelessWidget { final String? label; final String type; final String? value; + final String? replyId; final DSAlign align; final List borderRadius; final bool showRequestLocationButton; @@ -45,6 +47,7 @@ class DSRequestLocationBubble extends StatelessWidget { ), child: DSTextMessageBubble( text: value!, + replyId: replyId, align: align, borderRadius: borderRadius, style: style, diff --git a/lib/src/widgets/chat/ds_text_message_bubble.widget.dart b/lib/src/widgets/chat/ds_text_message_bubble.widget.dart index b82d4a50..35faf79d 100644 --- a/lib/src/widgets/chat/ds_text_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_text_message_bubble.widget.dart @@ -16,6 +16,7 @@ class DSTextMessageBubble extends StatefulWidget { final bool hasSpacer; final List borderRadius; final dynamic selectContent; + final String? replyId; final bool showSelect; final void Function(String, Map)? onSelected; final DSMessageBubbleStyle style; @@ -24,6 +25,7 @@ class DSTextMessageBubble extends StatefulWidget { Key? key, required this.text, required this.align, + this.replyId, this.borderRadius = const [DSBorderRadius.all], this.selectContent, this.hasSpacer = true, @@ -61,6 +63,7 @@ class _DSTextMessageBubbleState extends State { padding: EdgeInsets.zero, style: widget.style, hasSpacer: widget.hasSpacer, + replyId: widget.replyId, child: _buildText(), ); } diff --git a/lib/src/widgets/chat/ds_unsupported_content_message_bubble.widget.dart b/lib/src/widgets/chat/ds_unsupported_content_message_bubble.widget.dart index 34f16cc6..6ef590b7 100644 --- a/lib/src/widgets/chat/ds_unsupported_content_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_unsupported_content_message_bubble.widget.dart @@ -11,6 +11,7 @@ import 'ds_message_bubble.widget.dart'; class DSUnsupportedContentMessageBubble extends StatelessWidget { final DSAlign align; final Widget? leftWidget; + final String? replyId; final String? text; final TextOverflow overflow; final List borderRadius; @@ -21,6 +22,7 @@ class DSUnsupportedContentMessageBubble extends StatelessWidget { required this.align, this.leftWidget, this.text, + this.replyId, this.overflow = TextOverflow.ellipsis, this.borderRadius = const [DSBorderRadius.all], DSMessageBubbleStyle? style, @@ -37,6 +39,7 @@ class DSUnsupportedContentMessageBubble extends StatelessWidget { borderRadius: borderRadius, align: align, style: style, + replyId: replyId, child: Row( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/src/widgets/chat/ds_weblink.widget.dart b/lib/src/widgets/chat/ds_weblink.widget.dart index 0bd3689f..96f81a32 100644 --- a/lib/src/widgets/chat/ds_weblink.widget.dart +++ b/lib/src/widgets/chat/ds_weblink.widget.dart @@ -29,12 +29,16 @@ class DSWeblink extends StatelessWidget { /// Card styling to adjust custom colors final DSMessageBubbleStyle style; + /// replyId + final String? replyId; + DSWeblink({ Key? key, required this.title, required this.text, required this.align, required this.url, + this.replyId, this.borderRadius = const [DSBorderRadius.all], DSMessageBubbleStyle? style, }) : style = style ?? DSMessageBubbleStyle(), @@ -50,6 +54,7 @@ class DSWeblink extends StatelessWidget { return DSMessageBubble( align: align, + replyId: replyId, borderRadius: borderRadius, style: style, child: Column( diff --git a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart index 24129f8c..01ad15b1 100644 --- a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart +++ b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart @@ -42,6 +42,9 @@ class DSVideoMessageBubble extends StatefulWidget { // Unique id to message bubble final String uniqueId; + // reply id message + final String? replyId; + /// The video size final int mediaSize; @@ -61,6 +64,7 @@ class DSVideoMessageBubble extends StatefulWidget { required this.uniqueId, required this.mediaSize, this.appBarPhotoUri, + this.replyId, this.text, this.borderRadius = const [DSBorderRadius.all], this.shouldAuthenticate = false, @@ -121,6 +125,7 @@ class _DSVideoMessageBubbleState extends State borderRadius: widget.borderRadius, padding: EdgeInsets.zero, style: widget.style, + replyId: widget.replyId, child: LayoutBuilder( builder: (_, constraints) => Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/src/widgets/utils/ds_card.widget.dart b/lib/src/widgets/utils/ds_card.widget.dart index 596ce460..026fd9dd 100644 --- a/lib/src/widgets/utils/ds_card.widget.dart +++ b/lib/src/widgets/utils/ds_card.widget.dart @@ -43,6 +43,7 @@ class DSCard extends StatelessWidget { this.customer, this.showQuickReplyOptions = false, this.showRequestLocationButton = false, + this.replyId, }) : style = style ?? DSMessageBubbleStyle(); final String type; @@ -58,6 +59,7 @@ class DSCard extends StatelessWidget { final Map? customer; final bool showQuickReplyOptions; final bool showRequestLocationButton; + final String? replyId; @override Widget build(BuildContext context) { @@ -73,6 +75,7 @@ class DSCard extends StatelessWidget { align: align, borderRadius: borderRadius, style: style, + replyId: replyId, ); case DSMessageContentType.contact: @@ -91,6 +94,7 @@ class DSCard extends StatelessWidget { onOpenLink: onOpenLink, messageId: messageId, customer: customer, + replyId: content['inReplyTo']['id'], ); case DSMessageContentType.mediaLink: @@ -119,6 +123,7 @@ class DSCard extends StatelessWidget { text: content['text'], url: content['uri'], align: align, + replyId: replyId, borderRadius: borderRadius, style: style, ); @@ -131,6 +136,7 @@ class DSCard extends StatelessWidget { borderRadius: borderRadius, align: align, style: style, + replyId: replyId, ); case DSMessageContentType.ticket: return DSTicketMessage( @@ -154,6 +160,7 @@ class DSCard extends StatelessWidget { default: return DSUnsupportedContentMessageBubble( align: align, + replyId: replyId, borderRadius: borderRadius, style: style, ); @@ -178,6 +185,7 @@ class DSCard extends StatelessWidget { selectOptions: documentSelectModel.options, borderRadius: borderRadius, style: style, + replyId: replyId, showSelect: true, onSelected: onSelected, onOpenLink: onOpenLink, @@ -195,6 +203,7 @@ class DSCard extends StatelessWidget { child: DSTextMessageBubble( align: align, text: content['text'], + replyId: replyId, borderRadius: borderRadius, style: style, ), @@ -216,6 +225,7 @@ class DSCard extends StatelessWidget { borderRadius: borderRadius, selectContent: content, showSelect: true, + replyId: replyId, onSelected: onSelected, style: style, ); @@ -228,6 +238,7 @@ class DSCard extends StatelessWidget { address: content['address'], email: content['email'], align: align, + replyId: replyId, style: style, borderRadius: borderRadius, ); @@ -253,6 +264,7 @@ class DSCard extends StatelessWidget { align: align, borderRadius: borderRadius, style: style, + replyId: replyId, uniqueId: messageId, audioType: media.type, shouldAuthenticate: shouldAuthenticate, @@ -270,6 +282,7 @@ class DSCard extends StatelessWidget { : avatarConfig.sentAvatar, text: media.text, title: media.title, + replyId: replyId, borderRadius: borderRadius, style: style, shouldAuthenticate: shouldAuthenticate, @@ -286,6 +299,7 @@ class DSCard extends StatelessWidget { ? avatarConfig.receivedAvatar : avatarConfig.sentAvatar, text: media.text, + replyId: replyId, borderRadius: borderRadius, style: style, uniqueId: messageId ?? DateTime.now().toIso8601String(), @@ -296,6 +310,7 @@ class DSCard extends StatelessWidget { return DSFileMessageBubble( align: align, url: media.uri, + replyId: replyId, size: size, filename: media.title ?? '${media.uri.hashCode}.${DSFileService.getFileExtensionFromMime(media.type)}', @@ -313,6 +328,7 @@ class DSCard extends StatelessWidget { return DSRequestLocationBubble( label: 'Send location', + replyId: replyId, type: type, value: value, align: align, diff --git a/lib/src/widgets/utils/ds_group_card.widget.dart b/lib/src/widgets/utils/ds_group_card.widget.dart index 2e6dee95..7c8cc3fe 100644 --- a/lib/src/widgets/utils/ds_group_card.widget.dart +++ b/lib/src/widgets/utils/ds_group_card.widget.dart @@ -1,3 +1,4 @@ +import 'package:blip_ds/src/controllers/chat/ds_reply.controller.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -89,6 +90,7 @@ class DSGroupCard extends StatefulWidget { class _DSGroupCardState extends State { final List widgets = []; final showScrollBottomButton = false.obs; + final DSReplyController replyController = Get.put(DSReplyController()); @override void initState() { @@ -211,6 +213,12 @@ class _DSGroupCardState extends State { }; } } + + final listOfDSMessageItemModelList = groups.map((e) => e['msgs']).toList(); + replyController.messages.clear(); + for (List items in listOfDSMessageItemModelList) { + replyController.addMessages(items); + } groups.add(group); return groups; } From f5522198a95546ae05e8d6257cdc50d3a642e030 Mon Sep 17 00:00:00 2001 From: RaulRodrigo06 Date: Fri, 20 Oct 2023 15:35:23 -0300 Subject: [PATCH 02/22] chore: adding reply --- .../chat/ds_reply_container.widget.dart | 104 ++++++++++-------- 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/lib/src/widgets/chat/ds_reply_container.widget.dart b/lib/src/widgets/chat/ds_reply_container.widget.dart index a4528871..74d6f3ff 100644 --- a/lib/src/widgets/chat/ds_reply_container.widget.dart +++ b/lib/src/widgets/chat/ds_reply_container.widget.dart @@ -8,6 +8,7 @@ import '../../themes/colors/ds_colors.theme.dart'; import '../../themes/icons/ds_icons.dart'; import '../../utils/ds_message_content_type.util.dart'; import '../texts/ds_body_text.widget.dart'; +import '../texts/ds_caption_text.widget.dart'; class DSReplyContainer extends StatelessWidget { const DSReplyContainer({ @@ -29,46 +30,59 @@ class DSReplyContainer extends StatelessWidget { left: 8.0, right: 8.0, ), - child: DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4.0), - border: Border.all( - color: style.isLightBubbleBackground(align) - ? DSColors.contentGhost - : DSColors.contentDisable, - ), - color: style.isLightBubbleBackground(align) - ? DSColors.surface3 - : DSColors.contentDefault, - ), - child: IntrinsicHeight( - child: Row( + child: Column( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Container( - decoration: const ShapeDecoration( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(8.0), - bottomLeft: Radius.circular(8.0), + const Icon(DSIcons.undo_outline), + const SizedBox(width: 8.0), + DSCaptionText('Reply', fontStyle: FontStyle.italic), + ], + ), + const SizedBox(height: 4.0), + DecoratedBox( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4.0), + border: Border.all( + color: style.isLightBubbleBackground(align) + ? DSColors.contentGhost + : DSColors.contentDisable, + ), + color: style.isLightBubbleBackground(align) + ? DSColors.surface3 + : DSColors.contentDefault, + ), + child: IntrinsicHeight( + child: Row( + children: [ + Container( + decoration: const ShapeDecoration( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(8.0), + bottomLeft: Radius.circular(8.0), + ), + ), + color: DSColors.primary, ), + width: 4.0, ), - color: DSColors.primary, - ), - width: 4.0, - ), - Flexible( - child: Padding( - padding: const EdgeInsets.only( - top: 8.0, - left: 8.0, - bottom: 8.0, + Flexible( + child: Padding( + padding: const EdgeInsets.only( + top: 8.0, + left: 8.0, + bottom: 8.0, + ), + child: _replyWidget(replyId, style, align), + ), ), - child: _replyWidget(replyId, style, align), - ), + ], ), - ], + ), ), - ), + ], ), ); } @@ -86,9 +100,7 @@ Widget _replyWidget( case DSMessageContentType.textPlain: return DSBodyText( message?.content is String ? message?.content : '**********', - color: style.isLightBubbleBackground(align) - ? DSColors.contentDefault - : DSColors.neutralLightSnow, + color: _color(align, style), ); default: return Row( @@ -96,21 +108,23 @@ Widget _replyWidget( children: [ Icon( DSIcons.warning_outline, - color: style.isLightBubbleBackground(align) - ? DSColors.contentDefault - : DSColors.neutralLightSnow, + color: _color(align, style), ), - const SizedBox(width: 8), + const SizedBox(width: 8.0), Flexible( child: DSBodyText( - 'Falha ao carregar mensagem', + 'Failed to load message', overflow: TextOverflow.visible, - color: style.isLightBubbleBackground(align) - ? DSColors.contentDefault - : DSColors.neutralLightSnow, + color: _color(align, style), ), ), ], ); } } + +Color _color(DSAlign align, DSMessageBubbleStyle style) { + return style.isLightBubbleBackground(align) + ? DSColors.contentDefault + : DSColors.surface1; +} From a4bb774eae47adbf259e2dbf63ff68d82ea2f3fb Mon Sep 17 00:00:00 2001 From: RaulRodrigo06 Date: Fri, 20 Oct 2023 15:40:33 -0300 Subject: [PATCH 03/22] fix: fixing color reply icon and text based on bubble color --- .../widgets/chat/ds_reply_container.widget.dart | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/src/widgets/chat/ds_reply_container.widget.dart b/lib/src/widgets/chat/ds_reply_container.widget.dart index 74d6f3ff..7d2b4cdc 100644 --- a/lib/src/widgets/chat/ds_reply_container.widget.dart +++ b/lib/src/widgets/chat/ds_reply_container.widget.dart @@ -35,9 +35,20 @@ class DSReplyContainer extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - const Icon(DSIcons.undo_outline), + Icon( + DSIcons.undo_outline, + color: style.isLightBubbleBackground(align) + ? DSColors.neutralDarkCity + : DSColors.neutralLightSnow, + ), const SizedBox(width: 8.0), - DSCaptionText('Reply', fontStyle: FontStyle.italic), + DSCaptionText( + 'Reply', + fontStyle: FontStyle.italic, + color: style.isLightBubbleBackground(align) + ? DSColors.neutralDarkCity + : DSColors.neutralLightSnow, + ), ], ), const SizedBox(height: 4.0), From 5fa169642f0654607d9344531024890980d5ed9c Mon Sep 17 00:00:00 2001 From: RaulRodrigo06 Date: Mon, 23 Oct 2023 13:26:19 -0300 Subject: [PATCH 04/22] fix: fixing padding and text overflow on ds reply container --- lib/src/widgets/chat/ds_reply_container.widget.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/src/widgets/chat/ds_reply_container.widget.dart b/lib/src/widgets/chat/ds_reply_container.widget.dart index 7d2b4cdc..8dbc1d9f 100644 --- a/lib/src/widgets/chat/ds_reply_container.widget.dart +++ b/lib/src/widgets/chat/ds_reply_container.widget.dart @@ -25,10 +25,8 @@ class DSReplyContainer extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.only( - top: 8.0, - left: 8.0, - right: 8.0, + padding: const EdgeInsets.all( + 8.0, ), child: Column( children: [ @@ -112,6 +110,7 @@ Widget _replyWidget( return DSBodyText( message?.content is String ? message?.content : '**********', color: _color(align, style), + overflow: TextOverflow.visible, ); default: return Row( From 5b289057690820219f34589423d1530decb5326f Mon Sep 17 00:00:00 2001 From: RaulRodrigo06 Date: Mon, 23 Oct 2023 14:25:25 -0300 Subject: [PATCH 05/22] chore: removing empty line --- lib/src/themes/colors/ds_colors.theme.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/themes/colors/ds_colors.theme.dart b/lib/src/themes/colors/ds_colors.theme.dart index dbc496c4..e7dfd5d5 100644 --- a/lib/src/themes/colors/ds_colors.theme.dart +++ b/lib/src/themes/colors/ds_colors.theme.dart @@ -53,7 +53,6 @@ abstract class DSColors { static const Color contentDefault = Color(0xFF454545); static const Color contentGhost = Color(0xFF949494); - static const Color disabledText = Color(0xFF637798); static const Color disabledBg = Color(0xFFE8F2FF); static const Color contentDisable = Color(0xFF636363); From cdaac639e1133a3cbeaabb056bc7f5cf787fa415 Mon Sep 17 00:00:00 2001 From: RaulRodrigo06 Date: Thu, 26 Oct 2023 19:52:53 -0300 Subject: [PATCH 06/22] fix: passing reply content to ds card --- .../controllers/chat/ds_reply.controller.dart | 15 --------- lib/src/models/ds_message_item.model.dart | 5 +++ .../audio/ds_audio_message_bubble.widget.dart | 6 ++-- .../ds_application_json_bubble.widget.dart | 8 ++--- .../ds_contact_message_bubble.widget.dart | 7 ++-- .../chat/ds_file_message_bubble.widget.dart | 6 ++-- .../chat/ds_image_message_bubble.widget.dart | 6 ++-- .../ds_location_message_bubble.widget.dart | 6 ++-- .../chat/ds_message_bubble.widget.dart | 8 ++--- .../chat/ds_reply_container.widget.dart | 21 ++++++------ .../ds_request_location_bubble.widget.dart | 6 ++-- .../chat/ds_text_message_bubble.widget.dart | 6 ++-- ...pported_content_message_bubble.widget.dart | 6 ++-- lib/src/widgets/chat/ds_weblink.widget.dart | 8 ++--- .../video/ds_video_message_bubble.widget.dart | 8 ++--- lib/src/widgets/utils/ds_card.widget.dart | 32 +++++++++---------- .../widgets/utils/ds_group_card.widget.dart | 10 ++---- 17 files changed, 75 insertions(+), 89 deletions(-) delete mode 100644 lib/src/controllers/chat/ds_reply.controller.dart diff --git a/lib/src/controllers/chat/ds_reply.controller.dart b/lib/src/controllers/chat/ds_reply.controller.dart deleted file mode 100644 index 0586f3fb..00000000 --- a/lib/src/controllers/chat/ds_reply.controller.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:get/get.dart'; - -import '../../models/ds_message_item.model.dart'; - -class DSReplyController extends GetxController { - final List messages = []; - - void addMessages(List newMessages) { - messages.addAll(newMessages); - } - - DSMessageItemModel? getMessageById(String id) { - return messages.firstWhereOrNull((element) => element.id == id); - } -} diff --git a/lib/src/models/ds_message_item.model.dart b/lib/src/models/ds_message_item.model.dart index 856119b8..4dde23bf 100644 --- a/lib/src/models/ds_message_item.model.dart +++ b/lib/src/models/ds_message_item.model.dart @@ -26,6 +26,9 @@ class DSMessageItemModel { /// Customer data Map? customer; + /// The message content + dynamic replyContent; + /// Used to define if a message detail (typicament a messages date and time) should be displayed or not bool? hideMessageDetail; @@ -40,6 +43,7 @@ class DSMessageItemModel { this.content, this.customer, this.hideMessageDetail, + this.replyContent, }); factory DSMessageItemModel.fromJson(Map json) { @@ -52,6 +56,7 @@ class DSMessageItemModel { content: json['content'], status: DSDeliveryReportStatus.unknown.getValue(json['status']), hideMessageDetail: json['hideMessageDetail'], + replyContent: json['replyContent'], ); if (json.containsKey('customer')) { diff --git a/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart b/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart index aa01d4b4..e7193f6c 100644 --- a/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart +++ b/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart @@ -15,7 +15,7 @@ class DSAudioMessageBubble extends StatelessWidget { final String? uniqueId; final String audioType; final bool shouldAuthenticate; - final String? replyId; + final dynamic replyContent; DSAudioMessageBubble({ super.key, @@ -23,7 +23,7 @@ class DSAudioMessageBubble extends StatelessWidget { required this.align, required this.audioType, this.uniqueId, - this.replyId, + this.replyContent, this.borderRadius = const [DSBorderRadius.all], this.shouldAuthenticate = false, final DSMessageBubbleStyle? style, @@ -37,7 +37,7 @@ class DSAudioMessageBubble extends StatelessWidget { return DSMessageBubble( borderRadius: borderRadius, align: align, - replyId: replyId, + replyContent: replyContent, style: style, padding: const EdgeInsets.only( left: 4.0, diff --git a/lib/src/widgets/chat/ds_application_json_bubble.widget.dart b/lib/src/widgets/chat/ds_application_json_bubble.widget.dart index 2ec30d9c..307f3fbf 100644 --- a/lib/src/widgets/chat/ds_application_json_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_application_json_bubble.widget.dart @@ -15,14 +15,14 @@ class DSApplicationJsonMessageBubble extends StatelessWidget { required this.borderRadius, required this.content, this.status, - this.replyId, + this.replyContent, DSMessageBubbleStyle? style, }) : style = style ?? DSMessageBubbleStyle(); final DSAlign align; final List borderRadius; final Map content; - final String? replyId; + final dynamic replyContent; final DSDeliveryReportStatus? status; final DSMessageBubbleStyle style; @@ -33,7 +33,7 @@ class DSApplicationJsonMessageBubble extends StatelessWidget { opacity: status == DSDeliveryReportStatus.failed ? .3 : 1, child: DSUnsupportedContentMessageBubble( align: align, - replyId: replyId, + replyContent: replyContent, borderRadius: borderRadius, style: style, overflow: TextOverflow.visible, @@ -53,7 +53,7 @@ class DSApplicationJsonMessageBubble extends StatelessWidget { align: align, borderRadius: borderRadius, style: style, - replyId: replyId, + replyContent: replyContent, ); } } diff --git a/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart b/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart index 664ca2d1..80b83553 100644 --- a/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart @@ -6,7 +6,7 @@ class DSContactMessageBubble extends StatelessWidget { final String? phone; final String? email; final String? address; - final String? replyId; + final dynamic replyContent; final DSAlign align; final List borderRadius; final DSMessageBubbleStyle style; @@ -18,7 +18,7 @@ class DSContactMessageBubble extends StatelessWidget { required this.address, required this.email, required this.align, - this.replyId, + this.replyContent, this.borderRadius = const [DSBorderRadius.all], DSMessageBubbleStyle? style, }) : style = style ?? DSMessageBubbleStyle(); @@ -31,7 +31,7 @@ class DSContactMessageBubble extends StatelessWidget { padding: const EdgeInsets.all(16.0), shouldUseDefaultSize: true, style: style, - replyId: replyId, + replyContent: replyContent, child: _buildContactCard(), ); } @@ -53,6 +53,7 @@ class DSContactMessageBubble extends StatelessWidget { ), ), const SizedBox(height: 16.0), + /// TODO(format): Format phone number if (phone != null) Padding( diff --git a/lib/src/widgets/chat/ds_file_message_bubble.widget.dart b/lib/src/widgets/chat/ds_file_message_bubble.widget.dart index 21b63e25..6c6a0e5a 100644 --- a/lib/src/widgets/chat/ds_file_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_file_message_bubble.widget.dart @@ -22,7 +22,7 @@ class DSFileMessageBubble extends StatelessWidget { final List borderRadius; final DSMessageBubbleStyle style; final bool shouldAuthenticate; - final String? replyId; + final dynamic replyContent; /// Creates a Design System's [DSMessageBubble] used on files other than image, audio, or video DSFileMessageBubble({ @@ -31,7 +31,7 @@ class DSFileMessageBubble extends StatelessWidget { required this.url, required this.size, required this.filename, - this.replyId, + this.replyContent, this.borderRadius = const [DSBorderRadius.all], this.shouldAuthenticate = false, DSMessageBubbleStyle? style, @@ -48,7 +48,7 @@ class DSFileMessageBubble extends StatelessWidget { ), child: DSMessageBubble( borderRadius: borderRadius, - replyId: replyId, + replyContent: replyContent, padding: EdgeInsets.zero, align: align, style: style, diff --git a/lib/src/widgets/chat/ds_image_message_bubble.widget.dart b/lib/src/widgets/chat/ds_image_message_bubble.widget.dart index d558f00d..d97bf801 100644 --- a/lib/src/widgets/chat/ds_image_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_image_message_bubble.widget.dart @@ -29,7 +29,7 @@ class DSImageMessageBubble extends StatefulWidget { this.showSelect = false, this.onSelected, this.onOpenLink, - this.replyId, + this.replyContent, this.shouldAuthenticate = false, this.mediaType, this.imageMaxHeight, @@ -44,7 +44,7 @@ class DSImageMessageBubble extends StatefulWidget { final String? text; final String appBarText; final Uri? appBarPhotoUri; - final String? replyId; + final dynamic replyContent; final DSMessageBubbleStyle style; final List selectOptions; final bool showSelect; @@ -86,7 +86,7 @@ class _DSImageMessageBubbleState extends State defaultMaxSize: 360.0, shouldUseDefaultSize: true, align: widget.align, - replyId:widget.replyId, + replyContent: widget.replyContent, borderRadius: widget.borderRadius, padding: EdgeInsets.zero, hasSpacer: widget.hasSpacer, diff --git a/lib/src/widgets/chat/ds_location_message_bubble.widget.dart b/lib/src/widgets/chat/ds_location_message_bubble.widget.dart index 688361b9..924e666e 100644 --- a/lib/src/widgets/chat/ds_location_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_location_message_bubble.widget.dart @@ -16,7 +16,7 @@ class DSLocationMessageBubble extends StatelessWidget { final DSAlign align; final DSMessageBubbleStyle style; final String? title; - final String? replyId; + final dynamic replyContent; final String latitude; final String longitude; final List borderRadius; @@ -26,7 +26,7 @@ class DSLocationMessageBubble extends StatelessWidget { required this.align, required this.latitude, required this.longitude, - this.replyId, + this.replyContent, this.borderRadius = const [DSBorderRadius.all], DSMessageBubbleStyle? style, this.title, @@ -58,7 +58,7 @@ class DSLocationMessageBubble extends StatelessWidget { defaultMaxSize: 240.0, defaultMinSize: 240.0, borderRadius: borderRadius, - replyId: replyId, + replyContent: replyContent, padding: EdgeInsets.zero, align: align, style: style, diff --git a/lib/src/widgets/chat/ds_message_bubble.widget.dart b/lib/src/widgets/chat/ds_message_bubble.widget.dart index bcea3632..07f27c9e 100644 --- a/lib/src/widgets/chat/ds_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_message_bubble.widget.dart @@ -11,7 +11,7 @@ import '../animations/ds_animated_size.widget.dart'; class DSMessageBubble extends StatelessWidget { final DSAlign align; final Widget child; - final String? replyId; + final dynamic replyContent; final List borderRadius; final EdgeInsets padding; final bool shouldUseDefaultSize; @@ -24,7 +24,7 @@ class DSMessageBubble extends StatelessWidget { Key? key, required this.align, required this.child, - this.replyId, + this.replyContent, this.borderRadius = const [DSBorderRadius.all], this.padding = const EdgeInsets.symmetric( vertical: 8.0, @@ -67,9 +67,9 @@ class DSMessageBubble extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (replyId != null) + if (replyContent != null) DSReplyContainer( - replyId: replyId!, + replyContent: replyContent!, style: style, align: align, ), diff --git a/lib/src/widgets/chat/ds_reply_container.widget.dart b/lib/src/widgets/chat/ds_reply_container.widget.dart index 8dbc1d9f..ff0fb12e 100644 --- a/lib/src/widgets/chat/ds_reply_container.widget.dart +++ b/lib/src/widgets/chat/ds_reply_container.widget.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import '../../controllers/chat/ds_reply.controller.dart'; import '../../enums/ds_align.enum.dart'; import '../../models/ds_message_bubble_style.model.dart'; import '../../themes/colors/ds_colors.theme.dart'; @@ -13,13 +11,13 @@ import '../texts/ds_caption_text.widget.dart'; class DSReplyContainer extends StatelessWidget { const DSReplyContainer({ super.key, - required this.replyId, + required this.replyContent, required this.style, required this.align, }); final DSAlign align; - final String replyId; + final dynamic replyContent; final DSMessageBubbleStyle style; @override @@ -84,7 +82,7 @@ class DSReplyContainer extends StatelessWidget { left: 8.0, bottom: 8.0, ), - child: _replyWidget(replyId, style, align), + child: _replyWidget(replyContent, style, align), ), ), ], @@ -98,17 +96,18 @@ class DSReplyContainer extends StatelessWidget { } Widget _replyWidget( - String id, + dynamic replyContent, DSMessageBubbleStyle style, DSAlign align, ) { - final replyController = Get.find(); - final message = replyController.getMessageById(id); - - switch (message?.type) { + final replyType = + replyContent != '' ? replyContent['content']['replied']['type'] : ''; + switch (replyType) { case DSMessageContentType.textPlain: return DSBodyText( - message?.content is String ? message?.content : '**********', + replyContent['content']['replied']['value'] is String + ? replyContent['content']['replied']['value'] + : '**********', color: _color(align, style), overflow: TextOverflow.visible, ); diff --git a/lib/src/widgets/chat/ds_request_location_bubble.widget.dart b/lib/src/widgets/chat/ds_request_location_bubble.widget.dart index d8ceba85..b43455d6 100644 --- a/lib/src/widgets/chat/ds_request_location_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_request_location_bubble.widget.dart @@ -14,7 +14,7 @@ class DSRequestLocationBubble extends StatelessWidget { required this.label, required this.value, required this.align, - this.replyId, + this.replyContent, this.type = DSMessageContentType.textPlain, this.borderRadius = const [DSBorderRadius.all], this.showRequestLocationButton = false, @@ -24,7 +24,7 @@ class DSRequestLocationBubble extends StatelessWidget { final String? label; final String type; final String? value; - final String? replyId; + final dynamic replyContent; final DSAlign align; final List borderRadius; final bool showRequestLocationButton; @@ -47,7 +47,7 @@ class DSRequestLocationBubble extends StatelessWidget { ), child: DSTextMessageBubble( text: value!, - replyId: replyId, + replyContent: replyContent, align: align, borderRadius: borderRadius, style: style, diff --git a/lib/src/widgets/chat/ds_text_message_bubble.widget.dart b/lib/src/widgets/chat/ds_text_message_bubble.widget.dart index 35faf79d..a1c5c98b 100644 --- a/lib/src/widgets/chat/ds_text_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_text_message_bubble.widget.dart @@ -16,7 +16,7 @@ class DSTextMessageBubble extends StatefulWidget { final bool hasSpacer; final List borderRadius; final dynamic selectContent; - final String? replyId; + final dynamic replyContent; final bool showSelect; final void Function(String, Map)? onSelected; final DSMessageBubbleStyle style; @@ -25,7 +25,7 @@ class DSTextMessageBubble extends StatefulWidget { Key? key, required this.text, required this.align, - this.replyId, + this.replyContent, this.borderRadius = const [DSBorderRadius.all], this.selectContent, this.hasSpacer = true, @@ -63,7 +63,7 @@ class _DSTextMessageBubbleState extends State { padding: EdgeInsets.zero, style: widget.style, hasSpacer: widget.hasSpacer, - replyId: widget.replyId, + replyContent: widget.replyContent, child: _buildText(), ); } diff --git a/lib/src/widgets/chat/ds_unsupported_content_message_bubble.widget.dart b/lib/src/widgets/chat/ds_unsupported_content_message_bubble.widget.dart index 6ef590b7..160b4d5f 100644 --- a/lib/src/widgets/chat/ds_unsupported_content_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_unsupported_content_message_bubble.widget.dart @@ -11,7 +11,7 @@ import 'ds_message_bubble.widget.dart'; class DSUnsupportedContentMessageBubble extends StatelessWidget { final DSAlign align; final Widget? leftWidget; - final String? replyId; + final dynamic replyContent; final String? text; final TextOverflow overflow; final List borderRadius; @@ -22,7 +22,7 @@ class DSUnsupportedContentMessageBubble extends StatelessWidget { required this.align, this.leftWidget, this.text, - this.replyId, + this.replyContent, this.overflow = TextOverflow.ellipsis, this.borderRadius = const [DSBorderRadius.all], DSMessageBubbleStyle? style, @@ -39,7 +39,7 @@ class DSUnsupportedContentMessageBubble extends StatelessWidget { borderRadius: borderRadius, align: align, style: style, - replyId: replyId, + replyContent: replyContent, child: Row( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/src/widgets/chat/ds_weblink.widget.dart b/lib/src/widgets/chat/ds_weblink.widget.dart index 96f81a32..1bcb3953 100644 --- a/lib/src/widgets/chat/ds_weblink.widget.dart +++ b/lib/src/widgets/chat/ds_weblink.widget.dart @@ -29,8 +29,8 @@ class DSWeblink extends StatelessWidget { /// Card styling to adjust custom colors final DSMessageBubbleStyle style; - /// replyId - final String? replyId; + /// replyContent + final dynamic replyContent; DSWeblink({ Key? key, @@ -38,7 +38,7 @@ class DSWeblink extends StatelessWidget { required this.text, required this.align, required this.url, - this.replyId, + this.replyContent, this.borderRadius = const [DSBorderRadius.all], DSMessageBubbleStyle? style, }) : style = style ?? DSMessageBubbleStyle(), @@ -54,7 +54,7 @@ class DSWeblink extends StatelessWidget { return DSMessageBubble( align: align, - replyId: replyId, + replyContent: replyContent, borderRadius: borderRadius, style: style, child: Column( diff --git a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart index ea037bad..b5e9afaa 100644 --- a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart +++ b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart @@ -39,8 +39,8 @@ class DSVideoMessageBubble extends StatefulWidget { /// Style for bubble final DSMessageBubbleStyle style; - // reply id message - final String? replyId; + // reply id message + final dynamic replyContent; /// The video size final int mediaSize; @@ -63,7 +63,7 @@ class DSVideoMessageBubble extends StatefulWidget { required this.appBarText, required this.mediaSize, this.appBarPhotoUri, - this.replyId, + this.replyContent, this.type = 'video/mp4', this.text, this.borderRadius = const [DSBorderRadius.all], @@ -125,7 +125,7 @@ class _DSVideoMessageBubbleState extends State borderRadius: widget.borderRadius, padding: EdgeInsets.zero, style: widget.style, - replyId: widget.replyId, + replyContent: widget.replyContent, child: LayoutBuilder( builder: (_, constraints) => Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/src/widgets/utils/ds_card.widget.dart b/lib/src/widgets/utils/ds_card.widget.dart index 6532b391..2c2d3966 100644 --- a/lib/src/widgets/utils/ds_card.widget.dart +++ b/lib/src/widgets/utils/ds_card.widget.dart @@ -43,7 +43,7 @@ class DSCard extends StatelessWidget { this.customer, this.showQuickReplyOptions = false, this.showRequestLocationButton = false, - this.replyId, + this.replyContent, }) : style = style ?? DSMessageBubbleStyle(); final String type; @@ -59,7 +59,7 @@ class DSCard extends StatelessWidget { final Map? customer; final bool showQuickReplyOptions; final bool showRequestLocationButton; - final String? replyId; + final dynamic replyContent; @override Widget build(BuildContext context) { @@ -75,7 +75,7 @@ class DSCard extends StatelessWidget { align: align, borderRadius: borderRadius, style: style, - replyId: replyId, + replyContent: replyContent, ); case DSMessageContentType.contact: @@ -94,7 +94,7 @@ class DSCard extends StatelessWidget { onOpenLink: onOpenLink, messageId: messageId, customer: customer, - replyId: content['inReplyTo']['id'], + replyContent: replyContent, ); case DSMessageContentType.mediaLink: @@ -123,7 +123,7 @@ class DSCard extends StatelessWidget { text: content['text'], url: content['uri'], align: align, - replyId: replyId, + replyContent: replyContent, borderRadius: borderRadius, style: style, ); @@ -136,7 +136,7 @@ class DSCard extends StatelessWidget { borderRadius: borderRadius, align: align, style: style, - replyId: replyId, + replyContent: replyContent, ); case DSMessageContentType.ticket: return DSTicketMessage( @@ -160,7 +160,7 @@ class DSCard extends StatelessWidget { default: return DSUnsupportedContentMessageBubble( align: align, - replyId: replyId, + replyContent: replyContent, borderRadius: borderRadius, style: style, ); @@ -185,7 +185,7 @@ class DSCard extends StatelessWidget { selectOptions: documentSelectModel.options, borderRadius: borderRadius, style: style, - replyId: replyId, + replyContent: replyContent, showSelect: true, onSelected: onSelected, onOpenLink: onOpenLink, @@ -204,7 +204,7 @@ class DSCard extends StatelessWidget { child: DSTextMessageBubble( align: align, text: content['text'], - replyId: replyId, + replyContent: replyContent, borderRadius: borderRadius, style: style, ), @@ -226,7 +226,7 @@ class DSCard extends StatelessWidget { borderRadius: borderRadius, selectContent: content, showSelect: true, - replyId: replyId, + replyContent: replyContent, onSelected: onSelected, style: style, ); @@ -239,7 +239,7 @@ class DSCard extends StatelessWidget { address: content['address'], email: content['email'], align: align, - replyId: replyId, + replyContent: replyContent, style: style, borderRadius: borderRadius, ); @@ -265,7 +265,7 @@ class DSCard extends StatelessWidget { align: align, borderRadius: borderRadius, style: style, - replyId: replyId, + replyContent: replyContent, uniqueId: messageId, audioType: media.type, shouldAuthenticate: shouldAuthenticate, @@ -283,7 +283,7 @@ class DSCard extends StatelessWidget { : avatarConfig.sentAvatar, text: media.text, title: media.title, - replyId: replyId, + replyContent: replyContent, borderRadius: borderRadius, style: style, shouldAuthenticate: shouldAuthenticate, @@ -302,7 +302,7 @@ class DSCard extends StatelessWidget { ? avatarConfig.receivedAvatar : avatarConfig.sentAvatar, text: media.text, - replyId: replyId, + replyContent: replyContent, borderRadius: borderRadius, style: style, mediaSize: size, @@ -312,7 +312,7 @@ class DSCard extends StatelessWidget { return DSFileMessageBubble( align: align, url: media.uri, - replyId: replyId, + replyContent: replyContent, size: size, filename: media.title ?? '${media.uri.hashCode}.${DSFileService.getFileExtensionFromMime(media.type)}', @@ -330,7 +330,7 @@ class DSCard extends StatelessWidget { return DSRequestLocationBubble( label: 'Send location', - replyId: replyId, + replyContent: replyContent, type: type, value: value, align: align, diff --git a/lib/src/widgets/utils/ds_group_card.widget.dart b/lib/src/widgets/utils/ds_group_card.widget.dart index 7c8cc3fe..db26e6bd 100644 --- a/lib/src/widgets/utils/ds_group_card.widget.dart +++ b/lib/src/widgets/utils/ds_group_card.widget.dart @@ -1,4 +1,3 @@ -import 'package:blip_ds/src/controllers/chat/ds_reply.controller.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -54,6 +53,7 @@ class DSGroupCard extends StatefulWidget { super.key, required this.documents, required this.isComposing, + this.ticketId, this.sortMessages = true, this.onSelected, this.onOpenLink, @@ -73,6 +73,7 @@ class DSGroupCard extends StatefulWidget { final bool Function(DSMessageItemModel, DSMessageItemModel) compareMessages; final bool isComposing; final bool sortMessages; + final String? ticketId; final void Function(String, Map)? onSelected; final void Function(Map)? onOpenLink; final bool hideOptions; @@ -90,7 +91,6 @@ class DSGroupCard extends StatefulWidget { class _DSGroupCardState extends State { final List widgets = []; final showScrollBottomButton = false.obs; - final DSReplyController replyController = Get.put(DSReplyController()); @override void initState() { @@ -214,11 +214,6 @@ class _DSGroupCardState extends State { } } - final listOfDSMessageItemModelList = groups.map((e) => e['msgs']).toList(); - replyController.messages.clear(); - for (List items in listOfDSMessageItemModelList) { - replyController.addMessages(items); - } groups.add(group); return groups; } @@ -268,6 +263,7 @@ class _DSGroupCardState extends State { onOpenLink: widget.onOpenLink, messageId: message.id, customer: message.customer, + replyContent: message.replyContent, ); final isLastMsg = msgCount == length; From 71d7247b512567346d019f03d8212dbc90cee924 Mon Sep 17 00:00:00 2001 From: RaulRodrigo06 Date: Thu, 26 Oct 2023 20:00:27 -0300 Subject: [PATCH 07/22] fix: removing unused attributte --- lib/src/widgets/utils/ds_group_card.widget.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/src/widgets/utils/ds_group_card.widget.dart b/lib/src/widgets/utils/ds_group_card.widget.dart index db26e6bd..4d5b81ee 100644 --- a/lib/src/widgets/utils/ds_group_card.widget.dart +++ b/lib/src/widgets/utils/ds_group_card.widget.dart @@ -53,7 +53,6 @@ class DSGroupCard extends StatefulWidget { super.key, required this.documents, required this.isComposing, - this.ticketId, this.sortMessages = true, this.onSelected, this.onOpenLink, @@ -73,7 +72,6 @@ class DSGroupCard extends StatefulWidget { final bool Function(DSMessageItemModel, DSMessageItemModel) compareMessages; final bool isComposing; final bool sortMessages; - final String? ticketId; final void Function(String, Map)? onSelected; final void Function(Map)? onOpenLink; final bool hideOptions; From ef5ab9ce048faf010dc0415a562d4c6354bdfbc9 Mon Sep 17 00:00:00 2001 From: RaulRodrigo06 Date: Thu, 16 Nov 2023 16:47:05 -0300 Subject: [PATCH 08/22] fix: removing unecessary reply content and fixing return --- lib/src/models/ds_message_item.model.dart | 5 ----- lib/src/widgets/chat/ds_reply_container.widget.dart | 8 ++------ lib/src/widgets/utils/ds_card.widget.dart | 2 +- lib/src/widgets/utils/ds_group_card.widget.dart | 1 - 4 files changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/src/models/ds_message_item.model.dart b/lib/src/models/ds_message_item.model.dart index aeaf1ebf..e4d01fcf 100644 --- a/lib/src/models/ds_message_item.model.dart +++ b/lib/src/models/ds_message_item.model.dart @@ -27,9 +27,6 @@ class DSMessageItemModel { /// Customer data Map? customer; - /// The message content - dynamic replyContent; - /// Used to define if a message detail (typicament a messages date and time) should be displayed or not bool? hideMessageDetail; @@ -44,7 +41,6 @@ class DSMessageItemModel { this.content, this.customer, this.hideMessageDetail, - this.replyContent, }); factory DSMessageItemModel.fromJson(Map json) { @@ -57,7 +53,6 @@ class DSMessageItemModel { content: json['content'], status: DSDeliveryReportStatus.unknown.getValue(json['status']), hideMessageDetail: json['hideMessageDetail'], - replyContent: json['replyContent'], ); if (json.containsKey('customer')) { diff --git a/lib/src/widgets/chat/ds_reply_container.widget.dart b/lib/src/widgets/chat/ds_reply_container.widget.dart index ff0fb12e..83199263 100644 --- a/lib/src/widgets/chat/ds_reply_container.widget.dart +++ b/lib/src/widgets/chat/ds_reply_container.widget.dart @@ -100,14 +100,10 @@ Widget _replyWidget( DSMessageBubbleStyle style, DSAlign align, ) { - final replyType = - replyContent != '' ? replyContent['content']['replied']['type'] : ''; - switch (replyType) { + switch (replyContent['type']) { case DSMessageContentType.textPlain: return DSBodyText( - replyContent['content']['replied']['value'] is String - ? replyContent['content']['replied']['value'] - : '**********', + replyContent['value'] is String ? replyContent['value'] : '**********', color: _color(align, style), overflow: TextOverflow.visible, ); diff --git a/lib/src/widgets/utils/ds_card.widget.dart b/lib/src/widgets/utils/ds_card.widget.dart index b0f996fb..c2120496 100644 --- a/lib/src/widgets/utils/ds_card.widget.dart +++ b/lib/src/widgets/utils/ds_card.widget.dart @@ -94,7 +94,7 @@ class DSCard extends StatelessWidget { onOpenLink: onOpenLink, messageId: messageId, customer: customer, - replyContent: replyContent, + replyContent: content['inReplyTo'], ); case DSMessageContentType.mediaLink: diff --git a/lib/src/widgets/utils/ds_group_card.widget.dart b/lib/src/widgets/utils/ds_group_card.widget.dart index b4f94dbb..c74dd0ed 100644 --- a/lib/src/widgets/utils/ds_group_card.widget.dart +++ b/lib/src/widgets/utils/ds_group_card.widget.dart @@ -261,7 +261,6 @@ class _DSGroupCardState extends State { onOpenLink: widget.onOpenLink, messageId: message.id, customer: message.customer, - replyContent: message.replyContent, ); final isLastMsg = msgCount == length; From 5b7567a80e9e07525c0b7ee18509d6a4c60f80a0 Mon Sep 17 00:00:00 2001 From: Andre Rossi Date: Tue, 21 Nov 2023 15:42:41 -0300 Subject: [PATCH 09/22] feat: created isLoading property --- .../chat/ds_audio_player.controller.dart | 1 + .../ds_image_message_bubble.controller.dart | 1 - .../ds_video_message_bubble.controller.dart | 2 +- lib/src/extensions/future.extension.dart | 11 ++ lib/src/models/ds_message_item.model.dart | 5 + .../animations/ds_uploading.widget.dart | 53 ++++++ .../chat/audio/ds_audio_player.widget.dart | 8 +- .../chat/ds_delivery_report_icon.widget.dart | 13 +- .../chat/ds_file_message_bubble.widget.dart | 63 ++++--- .../chat/ds_image_message_bubble.widget.dart | 56 +++--- .../video/ds_video_message_bubble.widget.dart | 163 ++++++++++++------ lib/src/widgets/utils/ds_card.widget.dart | 12 +- .../utils/ds_expanded_image.widget.dart | 67 ++++--- .../widgets/utils/ds_group_card.widget.dart | 1 + 14 files changed, 306 insertions(+), 150 deletions(-) create mode 100644 lib/src/extensions/future.extension.dart create mode 100644 lib/src/widgets/animations/ds_uploading.widget.dart diff --git a/lib/src/controllers/chat/ds_audio_player.controller.dart b/lib/src/controllers/chat/ds_audio_player.controller.dart index 0f12ee37..53b86a42 100644 --- a/lib/src/controllers/chat/ds_audio_player.controller.dart +++ b/lib/src/controllers/chat/ds_audio_player.controller.dart @@ -6,6 +6,7 @@ class DSAudioPlayerController extends GetxController { final audioSpeed = RxDouble(1.0); final player = AudioPlayer(); final isInitialized = RxBool(false); + final isLoadingAudio = RxBool(false); /// Collects the data useful for displaying in a SeekBar widget. /// diff --git a/lib/src/controllers/chat/ds_image_message_bubble.controller.dart b/lib/src/controllers/chat/ds_image_message_bubble.controller.dart index 5a0fb230..e2a7ad91 100644 --- a/lib/src/controllers/chat/ds_image_message_bubble.controller.dart +++ b/lib/src/controllers/chat/ds_image_message_bubble.controller.dart @@ -13,7 +13,6 @@ class DSImageMessageBubbleController extends GetxController { final maximumProgress = RxInt(0); final downloadProgress = RxInt(0); final localPath = RxnString(); - final String url; final String? mediaType; final bool shouldAuthenticate; diff --git a/lib/src/controllers/chat/ds_video_message_bubble.controller.dart b/lib/src/controllers/chat/ds_video_message_bubble.controller.dart index bade8679..a3d40bcf 100644 --- a/lib/src/controllers/chat/ds_video_message_bubble.controller.dart +++ b/lib/src/controllers/chat/ds_video_message_bubble.controller.dart @@ -109,7 +109,7 @@ class DSVideoMessageBubbleController { hasError.value = !isSuccess; } - _generateThumbnail(outputFile.path); + await _generateThumbnail(outputFile.path); } catch (_) { hasError.value = true; diff --git a/lib/src/extensions/future.extension.dart b/lib/src/extensions/future.extension.dart new file mode 100644 index 00000000..d626361b --- /dev/null +++ b/lib/src/extensions/future.extension.dart @@ -0,0 +1,11 @@ +import 'package:get/get.dart'; + +extension FutureExtension on Future { + Future trueWhile(Rx rxValue) { + rxValue.value = true; + + return whenComplete( + () => rxValue.value = false, + ); + } +} diff --git a/lib/src/models/ds_message_item.model.dart b/lib/src/models/ds_message_item.model.dart index e4d01fcf..c67f6672 100644 --- a/lib/src/models/ds_message_item.model.dart +++ b/lib/src/models/ds_message_item.model.dart @@ -30,6 +30,9 @@ class DSMessageItemModel { /// Used to define if a message detail (typicament a messages date and time) should be displayed or not bool? hideMessageDetail; + /// if the media message is uploading + bool isUploading; + /// Creates a new Design System's [DSMessageItemModel] model DSMessageItemModel({ this.id, @@ -41,6 +44,7 @@ class DSMessageItemModel { this.content, this.customer, this.hideMessageDetail, + this.isUploading = false, }); factory DSMessageItemModel.fromJson(Map json) { @@ -53,6 +57,7 @@ class DSMessageItemModel { content: json['content'], status: DSDeliveryReportStatus.unknown.getValue(json['status']), hideMessageDetail: json['hideMessageDetail'], + isUploading: json['isUploading'] ?? false, ); if (json.containsKey('customer')) { diff --git a/lib/src/widgets/animations/ds_uploading.widget.dart b/lib/src/widgets/animations/ds_uploading.widget.dart new file mode 100644 index 00000000..ad41888f --- /dev/null +++ b/lib/src/widgets/animations/ds_uploading.widget.dart @@ -0,0 +1,53 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../themes/colors/ds_colors.theme.dart'; +import '../../themes/icons/ds_icons.dart'; + +class DSUploading extends StatefulWidget { + final double? size; + final Color? color; + + const DSUploading({ + super.key, + this.size, + this.color, + }); + + @override + State createState() => _DSUploadingState(); +} + +class _DSUploadingState extends State { + final _visible = RxBool(false); + + @override + void initState() { + super.initState(); + Timer.periodic( + const Duration(seconds: 1), + (timer) { + _visible.value = !_visible.value; + }, + ); + } + + @override + Widget build(BuildContext context) { + return Center( + child: Obx( + () => AnimatedOpacity( + opacity: _visible.value ? 1.0 : 0.0, + duration: const Duration(seconds: 1), + child: Icon( + DSIcons.upload_outline, + color: widget.color ?? DSColors.neutralLightSnow, + size: widget.size ?? 24.0, + ), + ), + ), + ); + } +} diff --git a/lib/src/widgets/chat/audio/ds_audio_player.widget.dart b/lib/src/widgets/chat/audio/ds_audio_player.widget.dart index 4718cdb3..7aaeb48b 100644 --- a/lib/src/widgets/chat/audio/ds_audio_player.widget.dart +++ b/lib/src/widgets/chat/audio/ds_audio_player.widget.dart @@ -7,6 +7,7 @@ import 'package:get/get.dart'; import 'package:just_audio/just_audio.dart'; import '../../../controllers/chat/ds_audio_player.controller.dart'; +import '../../../extensions/future.extension.dart'; import '../../../services/ds_auth.service.dart'; import '../../../services/ds_file.service.dart'; import '../../../services/ds_media_format.service.dart'; @@ -112,7 +113,9 @@ class _DSAudioPlayerState extends State ); try { - await _loadAudio(); + await _loadAudio().trueWhile( + _controller.isLoadingAudio, + ); _controller.isInitialized.value = true; } catch (_) { @@ -214,7 +217,8 @@ class _DSAudioPlayerState extends State ? _controller.player.play : () => {}, isLoading: [ProcessingState.loading, ProcessingState.buffering] - .contains(processingState), + .contains(processingState) || + _controller.isLoadingAudio.value, color: _controller.isInitialized.value ? widget.controlForegroundColor : DSColors.contentDisable, diff --git a/lib/src/widgets/chat/ds_delivery_report_icon.widget.dart b/lib/src/widgets/chat/ds_delivery_report_icon.widget.dart index 26484d61..d112b954 100644 --- a/lib/src/widgets/chat/ds_delivery_report_icon.widget.dart +++ b/lib/src/widgets/chat/ds_delivery_report_icon.widget.dart @@ -26,6 +26,12 @@ class DSDeliveryReportIcon extends StatelessWidget { const String path = 'assets/images'; switch (deliveryStatus) { + case DSDeliveryReportStatus.accepted: + return _getIcon( + '$path/check.svg', + DSColors.neutralMediumElephant, + ); + case DSDeliveryReportStatus.failed: return DSCaptionSmallText( 'Falha ao enviar mensagem.', @@ -67,9 +73,10 @@ class DSDeliveryReportIcon extends StatelessWidget { ); default: - return _getIcon( - '$path/check.svg', - DSColors.neutralMediumElephant, + return const Icon( + DSIcons.clock_outline, + size: 16, + color: DSColors.contentDefault, ); } } diff --git a/lib/src/widgets/chat/ds_file_message_bubble.widget.dart b/lib/src/widgets/chat/ds_file_message_bubble.widget.dart index 625ed975..a59332f6 100644 --- a/lib/src/widgets/chat/ds_file_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_file_message_bubble.widget.dart @@ -8,6 +8,7 @@ import '../../models/ds_message_bubble_style.model.dart'; import '../../services/ds_auth.service.dart'; import '../../themes/colors/ds_colors.theme.dart'; import '../animations/ds_fading_circle_loading.widget.dart'; +import '../animations/ds_uploading.widget.dart'; import '../texts/ds_body_text.widget.dart'; import '../texts/ds_caption_small_text.widget.dart'; import '../utils/ds_file_extension_icon.util.dart'; @@ -22,6 +23,7 @@ class DSFileMessageBubble extends StatelessWidget { final List borderRadius; final DSMessageBubbleStyle style; final bool shouldAuthenticate; + final bool isUploading; /// Creates a Design System's [DSMessageBubble] used on files other than image, audio, or video DSFileMessageBubble({ @@ -33,29 +35,36 @@ class DSFileMessageBubble extends StatelessWidget { this.borderRadius = const [DSBorderRadius.all], this.shouldAuthenticate = false, DSMessageBubbleStyle? style, + this.isUploading = false, }) : style = style ?? DSMessageBubbleStyle(), controller = DSFileMessageBubbleController(); @override Widget build(BuildContext context) { return GestureDetector( - onTap: () => controller.openFile( - url: url, - httpHeaders: shouldAuthenticate ? DSAuthService.httpHeaders : null, - ), - child: DSMessageBubble( - borderRadius: borderRadius, - padding: EdgeInsets.zero, - align: align, - style: style, - child: SizedBox( - height: 80.0, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - _buildIcon(), - _buildText(), - ], + onTap: () => !isUploading + ? controller.openFile( + url: url, + httpHeaders: + shouldAuthenticate ? DSAuthService.httpHeaders : null, + ) + : null, + child: Opacity( + opacity: !isUploading ? 1 : .5, + child: DSMessageBubble( + borderRadius: borderRadius, + padding: EdgeInsets.zero, + align: align, + style: style, + child: SizedBox( + height: 80.0, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + _buildIcon(), + _buildText(), + ], + ), ), ), ), @@ -71,15 +80,19 @@ class DSFileMessageBubble extends StatelessWidget { ? const DSFadingCircleLoading( color: DSColors.neutralDarkRooftop, ) - : Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - DSFileExtensionIcon( - filename: filename, - size: 40.0, + : !isUploading + ? Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + DSFileExtensionIcon( + filename: filename, + size: 40.0, + ), + ], + ) + : const DSUploading( + color: DSColors.neutralDarkRooftop, ), - ], - ), ), ); } diff --git a/lib/src/widgets/chat/ds_image_message_bubble.widget.dart b/lib/src/widgets/chat/ds_image_message_bubble.widget.dart index 260c9ecd..e3ba97db 100644 --- a/lib/src/widgets/chat/ds_image_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_image_message_bubble.widget.dart @@ -32,8 +32,7 @@ class DSImageMessageBubble extends StatefulWidget { this.onOpenLink, this.shouldAuthenticate = false, this.mediaType, - this.imageMaxHeight, - this.imageMinHeight, + this.isUploading = false, }) : style = style ?? DSMessageBubbleStyle(); final DSAlign align; @@ -51,8 +50,7 @@ class DSImageMessageBubble extends StatefulWidget { final void Function(Map)? onOpenLink; final bool shouldAuthenticate; final String? mediaType; - final double? imageMaxHeight; - final double? imageMinHeight; + final bool isUploading; @override State createState() => _DSImageMessageBubbleState(); @@ -82,7 +80,7 @@ class _DSImageMessageBubbleState extends State : DSColors.neutralLightSnow; return DSMessageBubble( - defaultMaxSize: 360.0, + defaultMaxSize: 240.0, shouldUseDefaultSize: true, align: widget.align, borderRadius: widget.borderRadius, @@ -94,32 +92,28 @@ class _DSImageMessageBubbleState extends State return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Obx( - () => _controller.localPath.value != null - ? DSExpandedImage( - appBarText: widget.appBarText, - appBarPhotoUri: widget.appBarPhotoUri, - url: _controller.localPath.value!, - maxHeight: widget.imageMaxHeight != null - ? widget.imageMaxHeight! - : widget.showSelect - ? 200.0 - : double.infinity, - minHeight: widget.imageMinHeight != null - ? widget.imageMinHeight! - : widget.showSelect - ? 200.0 - : 0.0, - align: widget.align, - style: widget.style, - isLoading: false, - shouldAuthenticate: widget.shouldAuthenticate, - ) - : DSCircularProgress( - currentProgress: _controller.downloadProgress, - maximumProgress: _controller.maximumProgress, - foregroundColor: foregroundColor, - ), + SizedBox( + width: 240, + height: 200, + child: Obx( + () => _controller.localPath.value != null + ? DSExpandedImage( + width: 240.0, + appBarText: widget.appBarText, + appBarPhotoUri: widget.appBarPhotoUri, + url: _controller.localPath.value!, + align: widget.align, + style: widget.style, + isLoading: false, + shouldAuthenticate: widget.shouldAuthenticate, + isUploading: widget.isUploading, + ) + : DSCircularProgress( + currentProgress: _controller.downloadProgress, + maximumProgress: _controller.maximumProgress, + foregroundColor: foregroundColor, + ), + ), ), if ((widget.title?.isNotEmpty ?? false) || (widget.text?.isNotEmpty ?? false)) diff --git a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart index 204850c0..6e3b1485 100644 --- a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart +++ b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart @@ -10,6 +10,7 @@ import '../../../models/ds_message_bubble_style.model.dart'; import '../../../services/ds_auth.service.dart'; import '../../../themes/colors/ds_colors.theme.dart'; import '../../../themes/icons/ds_icons.dart'; +import '../../animations/ds_uploading.widget.dart'; import '../../buttons/ds_button.widget.dart'; import '../../utils/ds_circular_progress.widget.dart'; import '../ds_message_bubble.widget.dart'; @@ -48,6 +49,9 @@ class DSVideoMessageBubble extends StatefulWidget { /// Indicates if the HTTP Requests should be authenticated or not. final bool shouldAuthenticate; + /// Indicates if the video file is uploading + final bool isUploading; + /// Card for the purpose of triggering a video to play. /// /// This widget is intended to display a video card from a url passed in the [url] parameter. @@ -65,6 +69,7 @@ class DSVideoMessageBubble extends StatefulWidget { this.borderRadius = const [DSBorderRadius.all], this.shouldAuthenticate = false, DSMessageBubbleStyle? style, + this.isUploading = false, }) : style = style ?? DSMessageBubbleStyle(); @override @@ -74,16 +79,22 @@ class DSVideoMessageBubble extends StatefulWidget { class _DSVideoMessageBubbleState extends State with AutomaticKeepAliveClientMixin { late final DSVideoMessageBubbleController _controller; + bool _isInitialized = false; @override void initState() { super.initState(); - _controller = DSVideoMessageBubbleController( - url: widget.url, - mediaSize: widget.mediaSize, - httpHeaders: widget.shouldAuthenticate ? DSAuthService.httpHeaders : null, - type: widget.type, - ); + + if (!widget.isUploading) { + _controller = DSVideoMessageBubbleController( + url: widget.url, + mediaSize: widget.mediaSize, + httpHeaders: + widget.shouldAuthenticate ? DSAuthService.httpHeaders : null, + type: widget.type, + ); + _isInitialized = true; + } } @override @@ -120,57 +131,95 @@ class _DSVideoMessageBubbleState extends State builder: (_, constraints) => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Obx( - () => SizedBox( - height: 240, - width: 240, - child: _controller.hasError.value - ? const Icon( - DSIcons.video_broken_outline, - size: 80.0, - color: DSColors.neutralDarkRooftop, - ) - : _controller.isDownloading.value - ? DSCircularProgress( - currentProgress: _controller.downloadProgress, - maximumProgress: _controller.maximumProgress, - foregroundColor: foregroundColor, - ) - : _controller.thumbnail.isEmpty - ? Center( - child: SizedBox( - height: 40, - child: DSButton( - leadingIcon: const Icon( - DSIcons.download_outline, - size: 20, - ), - backgroundColor: buttonBackgroundColor, - foregroundColor: buttonForegroundColor, - borderColor: buttonBorderColor, - label: _controller.size(), - onPressed: _controller.downloadVideo, - ), - ), - ) - : DSVideoBody( - align: widget.align, - appBarPhotoUri: widget.appBarPhotoUri, - appBarText: widget.appBarText, - url: widget.url, - shouldAuthenticate: widget.shouldAuthenticate, - thumbnail: Center( - child: Image.file( - File( - _controller.thumbnail.value, - ), - width: 240, - height: 240, - fit: BoxFit.cover, - ), - ), - ), - ), + SizedBox( + height: 240, + width: 240, + child: widget.isUploading + ? Stack( + children: [ + Image.file( + File( + widget.url, + ), + opacity: const AlwaysStoppedAnimation(.3), + width: 240, + height: 240, + fit: BoxFit.cover, + ), + const Positioned( + bottom: 10.0, + right: 10.0, + child: DSUploading(), + ), + const Center( + child: Icon( + DSIcons.video_outline, + color: DSColors.disabledBg, + size: 80.0, + ), + ), + ], + ) + : _isInitialized + ? Obx( + () => _controller.hasError.value + ? const Icon( + DSIcons.video_broken_outline, + size: 80.0, + color: DSColors.neutralDarkRooftop, + ) + : _controller.isLoadingThumbnail.value + ? const SizedBox.shrink() + : _controller.isDownloading.value + ? DSCircularProgress( + currentProgress: + _controller.downloadProgress, + maximumProgress: + _controller.maximumProgress, + foregroundColor: foregroundColor, + ) + : _controller.thumbnail.isEmpty + ? Center( + child: SizedBox( + height: 40, + child: DSButton( + leadingIcon: const Icon( + DSIcons.download_outline, + size: 20, + ), + backgroundColor: + buttonBackgroundColor, + foregroundColor: + buttonForegroundColor, + borderColor: + buttonBorderColor, + label: _controller.size(), + onPressed: + _controller.downloadVideo, + ), + ), + ) + : DSVideoBody( + align: widget.align, + appBarPhotoUri: + widget.appBarPhotoUri, + appBarText: widget.appBarText, + url: widget.url, + shouldAuthenticate: + widget.shouldAuthenticate, + thumbnail: Center( + child: Image.file( + File( + _controller.thumbnail.value, + ), + width: 240, + height: 240, + fit: BoxFit.cover, + ), + ), + ), + ) + : const SizedBox.shrink(), ), if (widget.text?.isNotEmpty ?? false) Padding( diff --git a/lib/src/widgets/utils/ds_card.widget.dart b/lib/src/widgets/utils/ds_card.widget.dart index 1159404b..3981fbc0 100644 --- a/lib/src/widgets/utils/ds_card.widget.dart +++ b/lib/src/widgets/utils/ds_card.widget.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import '../../enums/ds_align.enum.dart'; @@ -43,6 +45,7 @@ class DSCard extends StatelessWidget { this.customer, this.showQuickReplyOptions = false, this.showRequestLocationButton = false, + this.isUploading = false, }) : style = style ?? DSMessageBubbleStyle(); final String type; @@ -58,6 +61,7 @@ class DSCard extends StatelessWidget { final Map? customer; final bool showQuickReplyOptions; final bool showRequestLocationButton; + final bool isUploading; @override Widget build(BuildContext context) { @@ -250,7 +254,9 @@ class DSCard extends StatelessWidget { if (media.type.contains('audio')) { return DSAudioMessageBubble( - uri: Uri.parse(media.uri), + uri: media.uri.startsWith('http') + ? Uri.parse(media.uri) + : File(media.uri).uri, align: align, borderRadius: borderRadius, style: style, @@ -274,7 +280,7 @@ class DSCard extends StatelessWidget { style: style, shouldAuthenticate: shouldAuthenticate, mediaType: media.type, - imageMaxHeight: 300.0, + isUploading: isUploading, ); } else if (media.type.contains('video')) { return DSVideoMessageBubble( @@ -292,6 +298,7 @@ class DSCard extends StatelessWidget { style: style, mediaSize: size, shouldAuthenticate: shouldAuthenticate, + isUploading: isUploading, ); } else { return DSFileMessageBubble( @@ -303,6 +310,7 @@ class DSCard extends StatelessWidget { borderRadius: borderRadius, style: style, shouldAuthenticate: shouldAuthenticate, + isUploading: isUploading, ); } } diff --git a/lib/src/widgets/utils/ds_expanded_image.widget.dart b/lib/src/widgets/utils/ds_expanded_image.widget.dart index e411d8d4..36c72167 100644 --- a/lib/src/widgets/utils/ds_expanded_image.widget.dart +++ b/lib/src/widgets/utils/ds_expanded_image.widget.dart @@ -6,19 +6,20 @@ import 'package:get/get.dart'; import 'package:pinch_zoom/pinch_zoom.dart'; import '../../../blip_ds.dart'; +import '../animations/ds_uploading.widget.dart'; class DSExpandedImage extends StatelessWidget { final String appBarText; final String url; final BoxFit fit; final double width; - final double minHeight; - final double maxHeight; + final double height; final bool isLoading; final Uri? appBarPhotoUri; final DSMessageBubbleStyle style; final DSAlign align; final bool shouldAuthenticate; + final bool isUploading; final _error = RxBool(false); final _isAppBarVisible = RxBool(false); @@ -29,13 +30,13 @@ class DSExpandedImage extends StatelessWidget { required this.url, this.fit = BoxFit.cover, this.width = double.infinity, - this.maxHeight = double.infinity, - this.minHeight = 0.0, + this.height = double.infinity, this.isLoading = false, this.appBarPhotoUri, this.shouldAuthenticate = false, DSMessageBubbleStyle? style, DSAlign? align, + this.isUploading = false, }) : style = style ?? DSMessageBubbleStyle(), align = align ?? DSAlign.center; @@ -50,30 +51,40 @@ class DSExpandedImage extends StatelessWidget { _expandImage(); } }, - child: Container( - constraints: BoxConstraints( - maxHeight: maxHeight, - minHeight: minHeight, - ), - child: url.startsWith('http') - ? DSCachedNetworkImageView( - fit: fit, - width: width, - url: url, - placeholder: (_, __) => _buildLoading(), - onError: () => _error.value = true, - align: align, - style: style, - shouldAuthenticate: shouldAuthenticate, - ) - : Image.file( - File(url), - width: width, - fit: fit, - cacheWidth: 360, - errorBuilder: (_, __, ___) => _defaultErrorWidget(), - ), - ), + child: url.startsWith('http') + ? DSCachedNetworkImageView( + fit: fit, + width: width, + height: height, + url: url, + placeholder: (_, __) => _buildLoading(), + onError: () => _error.value = true, + align: align, + style: style, + shouldAuthenticate: shouldAuthenticate, + ) + : Stack( + children: [ + Image.file( + File(url), + width: width, + height: height, + fit: fit, + cacheWidth: 360, + errorBuilder: (_, __, ___) => _defaultErrorWidget(), + opacity: + isUploading ? const AlwaysStoppedAnimation(.3) : null, + ), + Positioned( + bottom: 10.0, + right: 10.0, + child: Visibility( + visible: isUploading, + child: const DSUploading(), + ), + ), + ], + ), ), ); diff --git a/lib/src/widgets/utils/ds_group_card.widget.dart b/lib/src/widgets/utils/ds_group_card.widget.dart index 5224557c..b61fc2ef 100644 --- a/lib/src/widgets/utils/ds_group_card.widget.dart +++ b/lib/src/widgets/utils/ds_group_card.widget.dart @@ -260,6 +260,7 @@ class _DSGroupCardState extends State { onOpenLink: widget.onOpenLink, messageId: message.id, customer: message.customer, + isUploading: message.isUploading, ); final isLastMsg = msgCount == length; From cb0323c04b2eb2ae7d0a87a6f68f127ecc978fca Mon Sep 17 00:00:00 2001 From: Andre Rossi Date: Wed, 22 Nov 2023 13:15:59 -0300 Subject: [PATCH 10/22] fix: set manually extension for ppt --- lib/src/services/ds_file.service.dart | 7 ++- .../chat/ds_file_message_bubble.widget.dart | 1 - .../chat/ds_message_bubble.widget.dart | 44 +++++++++---------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/lib/src/services/ds_file.service.dart b/lib/src/services/ds_file.service.dart index c4eeb2ef..0809f1e0 100644 --- a/lib/src/services/ds_file.service.dart +++ b/lib/src/services/ds_file.service.dart @@ -102,6 +102,9 @@ abstract class DSFileService { return null; } - static String getFileExtensionFromMime(String? mimeType) => - extensionFromMime(mimeType ?? ''); + static String getFileExtensionFromMime(String? mimeType) { + return mimeType == 'application/vnd.ms-powerpoint' + ? 'ppt' + : extensionFromMime(mimeType ?? ''); + } } diff --git a/lib/src/widgets/chat/ds_file_message_bubble.widget.dart b/lib/src/widgets/chat/ds_file_message_bubble.widget.dart index 21afe492..9007c39b 100644 --- a/lib/src/widgets/chat/ds_file_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_file_message_bubble.widget.dart @@ -108,7 +108,6 @@ class DSFileMessageBubble extends StatelessWidget { filename, color: color, textAlign: TextAlign.center, - isSelectable: true, shouldLinkify: false, ), Visibility( diff --git a/lib/src/widgets/chat/ds_message_bubble.widget.dart b/lib/src/widgets/chat/ds_message_bubble.widget.dart index 07f27c9e..c977fa6e 100644 --- a/lib/src/widgets/chat/ds_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_message_bubble.widget.dart @@ -1,4 +1,3 @@ -import 'package:blip_ds/src/widgets/chat/ds_reply_container.widget.dart'; import 'package:flutter/material.dart'; import '../../enums/ds_align.enum.dart'; @@ -7,6 +6,7 @@ import '../../models/ds_message_bubble_style.model.dart'; import '../../utils/ds_bubble.util.dart'; import '../../utils/ds_utils.util.dart'; import '../animations/ds_animated_size.widget.dart'; +import 'ds_reply_container.widget.dart'; class DSMessageBubble extends StatelessWidget { final DSAlign align; @@ -37,12 +37,21 @@ class DSMessageBubble extends StatelessWidget { this.hasSpacer = true, }) : super(key: key); - BorderRadius _getBorderRadius() { - return borderRadius.getCircularBorderRadius( - maxRadius: 22.0, - minRadius: 2.0, - ); - } + @override + Widget build(BuildContext context) => Column( + children: [ + Row( + mainAxisAlignment: align == DSAlign.right + ? MainAxisAlignment.end + : MainAxisAlignment.start, + children: DSBubbleUtils.addSpacer( + align: align, + hasSpacer: hasSpacer, + child: _messageContainer(), + ), + ), + ], + ); Widget _messageContainer() { return DSAnimatedSize( @@ -82,19 +91,10 @@ class DSMessageBubble extends StatelessWidget { ); } - @override - Widget build(BuildContext context) => Column( - children: [ - Row( - mainAxisAlignment: align == DSAlign.right - ? MainAxisAlignment.end - : MainAxisAlignment.start, - children: DSBubbleUtils.addSpacer( - align: align, - hasSpacer: hasSpacer, - child: _messageContainer(), - ), - ), - ], - ); + BorderRadius _getBorderRadius() { + return borderRadius.getCircularBorderRadius( + maxRadius: 22.0, + minRadius: 2.0, + ); + } } From 2bb958bb54fe9480ecce31f6ac9b03ae6ee8f735 Mon Sep 17 00:00:00 2001 From: Andre Rossi Date: Tue, 28 Nov 2023 10:03:18 -0300 Subject: [PATCH 11/22] feat: improve bubbles layout with reply --- .../audio/ds_audio_message_bubble.widget.dart | 72 +++++---- .../ds_contact_message_bubble.widget.dart | 81 +++++----- .../chat/ds_image_message_bubble.widget.dart | 145 +++++++++--------- .../ds_location_message_bubble.widget.dart | 77 +++++----- .../video/ds_video_message_bubble.widget.dart | 118 +++++++------- 5 files changed, 259 insertions(+), 234 deletions(-) diff --git a/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart b/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart index f33f142f..7d5bd194 100644 --- a/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart +++ b/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart @@ -37,43 +37,41 @@ class DSAudioMessageBubble extends StatelessWidget { align: align, replyContent: replyContent, style: style, - padding: const EdgeInsets.only( - left: 4.0, - right: 8.0, - top: 8.0, - bottom: 8.0, - ), - child: DSAudioPlayer( - uri: uri, - shouldAuthenticate: shouldAuthenticate, - controlForegroundColor: isLightBubbleBackground - ? DSColors.neutralDarkRooftop - : DSColors.neutralLightSnow, - labelColor: isLightBubbleBackground - ? DSColors.neutralDarkCity - : DSColors.neutralLightSnow, - bufferActiveTrackColor: isLightBubbleBackground - ? DSColors.neutralMediumWave - : DSColors.neutralMediumElephant, - bufferInactiveTrackColor: isLightBubbleBackground - ? DSColors.neutralDarkRooftop - : DSColors.neutralLightBox, - sliderActiveTrackColor: isLightBubbleBackground - ? DSColors.primaryNight - : DSColors.primaryLight, - sliderThumbColor: isLightBubbleBackground - ? DSColors.neutralDarkRooftop - : DSColors.neutralLightSnow, - speedForegroundColor: isLightBubbleBackground - ? DSColors.neutralDarkCity - : DSColors.neutralLightSnow, - speedBorderColor: isLightBubbleBackground - ? isDefaultBubbleColors - ? DSColors.neutralMediumSilver - : DSColors.neutralDarkCity - : isDefaultBubbleColors - ? DSColors.disabledText - : DSColors.neutralLightSnow, + padding: EdgeInsets.zero, + child: Padding( + padding: const EdgeInsets.fromLTRB(4.0, 8.0, 8.0, 8.0), + child: DSAudioPlayer( + uri: uri, + shouldAuthenticate: shouldAuthenticate, + controlForegroundColor: isLightBubbleBackground + ? DSColors.neutralDarkRooftop + : DSColors.neutralLightSnow, + labelColor: isLightBubbleBackground + ? DSColors.neutralDarkCity + : DSColors.neutralLightSnow, + bufferActiveTrackColor: isLightBubbleBackground + ? DSColors.neutralMediumWave + : DSColors.neutralMediumElephant, + bufferInactiveTrackColor: isLightBubbleBackground + ? DSColors.neutralDarkRooftop + : DSColors.neutralLightBox, + sliderActiveTrackColor: isLightBubbleBackground + ? DSColors.primaryNight + : DSColors.primaryLight, + sliderThumbColor: isLightBubbleBackground + ? DSColors.neutralDarkRooftop + : DSColors.neutralLightSnow, + speedForegroundColor: isLightBubbleBackground + ? DSColors.neutralDarkCity + : DSColors.neutralLightSnow, + speedBorderColor: isLightBubbleBackground + ? isDefaultBubbleColors + ? DSColors.neutralMediumSilver + : DSColors.neutralDarkCity + : isDefaultBubbleColors + ? DSColors.disabledText + : DSColors.neutralLightSnow, + ), ), ); } diff --git a/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart b/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart index 29d4ecd1..1c7f930e 100644 --- a/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart @@ -36,7 +36,7 @@ class DSContactMessageBubble extends StatelessWidget { return DSMessageBubble( align: align, borderRadius: borderRadius, - padding: const EdgeInsets.all(16.0), + padding: EdgeInsets.zero, shouldUseDefaultSize: true, style: style, replyContent: replyContent, @@ -48,44 +48,51 @@ class DSContactMessageBubble extends StatelessWidget { final foregroundColor = style.isLightBubbleBackground(align) ? DSColors.neutralDarkCity : DSColors.neutralLightSnow; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Visibility( - visible: name != null, - child: DSBodyText( - name, - fontWeight: DSFontWeights.semiBold, - color: foregroundColor, - overflow: TextOverflow.visible, - ), - ), - const SizedBox(height: 16.0), - /// TODO(format): Format phone number - if (phone != null) - Padding( - padding: const EdgeInsets.only(bottom: 4.0), - child: _buildContactField( - title: 'Telefone', - body: phone!, - foregroundColor: foregroundColor), + return Padding( + padding: const EdgeInsets.symmetric( + vertical: 8.0, + horizontal: 16.0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Visibility( + visible: name != null, + child: DSBodyText( + name, + fontWeight: DSFontWeights.semiBold, + color: foregroundColor, + overflow: TextOverflow.visible, + ), ), - if (email != null) - Padding( - padding: const EdgeInsets.only(bottom: 4.0), - child: _buildContactField( - title: 'E-mail', - body: email!, - foregroundColor: foregroundColor), - ), - if (address != null) - _buildContactField( - title: 'Endereço', - body: address!, - foregroundColor: foregroundColor, - ), - ], + const SizedBox(height: 16.0), + + /// TODO(format): Format phone number + if (phone != null) + Padding( + padding: const EdgeInsets.only(bottom: 4.0), + child: _buildContactField( + title: 'Telefone', + body: phone!, + foregroundColor: foregroundColor), + ), + if (email != null) + Padding( + padding: const EdgeInsets.only(bottom: 4.0), + child: _buildContactField( + title: 'E-mail', + body: email!, + foregroundColor: foregroundColor), + ), + if (address != null) + _buildContactField( + title: 'Endereço', + body: address!, + foregroundColor: foregroundColor, + ), + ], + ), ); } diff --git a/lib/src/widgets/chat/ds_image_message_bubble.widget.dart b/lib/src/widgets/chat/ds_image_message_bubble.widget.dart index 3d441bda..df2d0d0e 100644 --- a/lib/src/widgets/chat/ds_image_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_image_message_bubble.widget.dart @@ -92,79 +92,84 @@ class _DSImageMessageBubbleState extends State padding: EdgeInsets.zero, hasSpacer: widget.hasSpacer, style: widget.style, - child: LayoutBuilder( - builder: (_, constraints) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Obx( - () => _controller.localPath.value != null - ? DSExpandedImage( - appBarText: widget.appBarText, - appBarPhotoUri: widget.appBarPhotoUri, - url: _controller.localPath.value!, - maxHeight: widget.imageMaxHeight != null - ? widget.imageMaxHeight! - : widget.showSelect - ? 200.0 - : double.infinity, - minHeight: widget.imageMinHeight != null - ? widget.imageMinHeight! - : widget.showSelect - ? 200.0 - : 0.0, - align: widget.align, - style: widget.style, - isLoading: false, - shouldAuthenticate: widget.shouldAuthenticate, - ) - : DSCircularProgress( - currentProgress: _controller.downloadProgress, - maximumProgress: _controller.maximumProgress, - foregroundColor: foregroundColor, + child: Padding( + padding: widget.replyContent == null + ? EdgeInsets.zero + : const EdgeInsets.only(top: 8.0), + child: LayoutBuilder( + builder: (_, constraints) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Obx( + () => _controller.localPath.value != null + ? DSExpandedImage( + appBarText: widget.appBarText, + appBarPhotoUri: widget.appBarPhotoUri, + url: _controller.localPath.value!, + maxHeight: widget.imageMaxHeight != null + ? widget.imageMaxHeight! + : widget.showSelect + ? 200.0 + : double.infinity, + minHeight: widget.imageMinHeight != null + ? widget.imageMinHeight! + : widget.showSelect + ? 200.0 + : 0.0, + align: widget.align, + style: widget.style, + isLoading: false, + shouldAuthenticate: widget.shouldAuthenticate, + ) + : DSCircularProgress( + currentProgress: _controller.downloadProgress, + maximumProgress: _controller.maximumProgress, + foregroundColor: foregroundColor, + ), + ), + if ((widget.title?.isNotEmpty ?? false) || + (widget.text?.isNotEmpty ?? false)) + SizedBox( + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.title?.isNotEmpty ?? false) + DSCaptionText( + widget.title!, + color: foregroundColor, + isSelectable: true, + ), + if ((widget.text?.isNotEmpty ?? false) && + (widget.title?.isNotEmpty ?? false)) + const SizedBox( + height: 6.0, + ), + if (widget.text?.isNotEmpty ?? false) + DSShowMoreText( + text: widget.text!, + maxWidth: constraints.maxWidth, + align: widget.align, + style: widget.style, + ) + ], ), - ), - if ((widget.title?.isNotEmpty ?? false) || - (widget.text?.isNotEmpty ?? false)) - SizedBox( - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (widget.title?.isNotEmpty ?? false) - DSCaptionText( - widget.title!, - color: foregroundColor, - isSelectable: true, - ), - if ((widget.text?.isNotEmpty ?? false) && - (widget.title?.isNotEmpty ?? false)) - const SizedBox( - height: 6.0, - ), - if (widget.text?.isNotEmpty ?? false) - DSShowMoreText( - text: widget.text!, - maxWidth: constraints.maxWidth, - align: widget.align, - style: widget.style, - ) - ], ), ), - ), - if (widget.showSelect) - DSDocumentSelect( - align: widget.align, - options: widget.selectOptions, - onSelected: widget.onSelected, - onOpenLink: widget.onOpenLink, - style: widget.style, - ), - ], - ); - }, + if (widget.showSelect) + DSDocumentSelect( + align: widget.align, + options: widget.selectOptions, + onSelected: widget.onSelected, + onOpenLink: widget.onOpenLink, + style: widget.style, + ), + ], + ); + }, + ), ), ); } diff --git a/lib/src/widgets/chat/ds_location_message_bubble.widget.dart b/lib/src/widgets/chat/ds_location_message_bubble.widget.dart index 924e666e..f2a7d846 100644 --- a/lib/src/widgets/chat/ds_location_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_location_message_bubble.widget.dart @@ -62,46 +62,51 @@ class DSLocationMessageBubble extends StatelessWidget { padding: EdgeInsets.zero, align: align, style: style, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - hasValidCoordinates - ? DSCachedNetworkImageView( - url: - 'https://maps.googleapis.com/maps/api/staticmap?&size=360x360&markers=$latitude,$longitude&key=${DSAuthService.googleKey}', - placeholder: (_, __) => _buildLoading(), - align: align, - style: style, - ) - : SizedBox( - width: 240, - height: 240, - child: Icon( - DSIcons.file_image_broken_outline, - size: 80, - color: style.isLightBubbleBackground(align) - ? DSColors.neutralMediumElephant - : DSColors.neutralMediumCloud, + child: Padding( + padding: replyContent == null + ? EdgeInsets.zero + : const EdgeInsets.only(top: 8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + hasValidCoordinates + ? DSCachedNetworkImageView( + url: + 'https://maps.googleapis.com/maps/api/staticmap?&size=360x360&markers=$latitude,$longitude&key=${DSAuthService.googleKey}', + placeholder: (_, __) => _buildLoading(), + align: align, + style: style, + ) + : SizedBox( + width: 240, + height: 240, + child: Icon( + DSIcons.file_image_broken_outline, + size: 80, + color: style.isLightBubbleBackground(align) + ? DSColors.neutralMediumElephant + : DSColors.neutralMediumCloud, + ), ), + if (title?.isNotEmpty ?? false) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, ), - if (title?.isNotEmpty ?? false) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - child: Align( - alignment: Alignment.topLeft, - child: DSBodyText( - title!, - color: foregroundColor, - isSelectable: true, - overflow: TextOverflow.visible, + child: Align( + alignment: Alignment.topLeft, + child: DSBodyText( + title!, + color: foregroundColor, + isSelectable: true, + overflow: TextOverflow.visible, + ), ), ), - ), - ], + ], + ), ), ), ); diff --git a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart index 416489c1..2c3cfa8c 100644 --- a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart +++ b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart @@ -116,6 +116,8 @@ class _DSVideoMessageBubbleState extends State : DSColors.neutralDarkCity; return DSMessageBubble( + defaultMaxSize: 240, + shouldUseDefaultSize: true, align: widget.align, borderRadius: widget.borderRadius, padding: EdgeInsets.zero, @@ -126,68 +128,76 @@ class _DSVideoMessageBubbleState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ Obx( - () => SizedBox( - height: 240, - width: 240, - child: _controller.hasError.value - ? const Icon( - DSIcons.video_broken_outline, - size: 80.0, - color: DSColors.neutralDarkRooftop, - ) - : _controller.isDownloading.value - ? DSCircularProgress( - currentProgress: _controller.downloadProgress, - maximumProgress: _controller.maximumProgress, - foregroundColor: foregroundColor, - ) - : _controller.thumbnail.isEmpty - ? Center( - child: SizedBox( - height: 40, - child: DSButton( - leadingIcon: const Icon( - DSIcons.download_outline, - size: 20, + () => Padding( + padding: widget.replyContent == null + ? EdgeInsets.zero + : const EdgeInsets.only(top: 8.0), + child: SizedBox( + height: 240, + width: 240, + child: _controller.hasError.value + ? const Icon( + DSIcons.video_broken_outline, + size: 80.0, + color: DSColors.neutralDarkRooftop, + ) + : _controller.isDownloading.value + ? DSCircularProgress( + currentProgress: _controller.downloadProgress, + maximumProgress: _controller.maximumProgress, + foregroundColor: foregroundColor, + ) + : _controller.thumbnail.isEmpty + ? Center( + child: SizedBox( + height: 40, + child: DSButton( + leadingIcon: const Icon( + DSIcons.download_outline, + size: 20, + ), + backgroundColor: buttonBackgroundColor, + foregroundColor: buttonForegroundColor, + borderColor: buttonBorderColor, + label: _controller.size(), + onPressed: _controller.downloadVideo, ), - backgroundColor: buttonBackgroundColor, - foregroundColor: buttonForegroundColor, - borderColor: buttonBorderColor, - label: _controller.size(), - onPressed: _controller.downloadVideo, ), - ), - ) - : DSVideoBody( - align: widget.align, - appBarPhotoUri: widget.appBarPhotoUri, - appBarText: widget.appBarText, - url: widget.url, - shouldAuthenticate: widget.shouldAuthenticate, - thumbnail: Center( - child: Image.file( - File( - _controller.thumbnail.value, + ) + : DSVideoBody( + align: widget.align, + appBarPhotoUri: widget.appBarPhotoUri, + appBarText: widget.appBarText, + url: widget.url, + shouldAuthenticate: widget.shouldAuthenticate, + thumbnail: Center( + child: Image.file( + File( + _controller.thumbnail.value, + ), + width: 240, + height: 240, + fit: BoxFit.cover, ), - width: 240, - height: 240, - fit: BoxFit.cover, ), ), - ), + ), ), ), if (widget.text?.isNotEmpty ?? false) - Padding( - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, - ), - child: DSShowMoreText( - text: widget.text!, - align: widget.align, - style: widget.style, - maxWidth: constraints.maxWidth, + SizedBox( + width: 240, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8.0, + horizontal: 16.0, + ), + child: DSShowMoreText( + text: widget.text!, + align: widget.align, + style: widget.style, + maxWidth: constraints.maxWidth, + ), ), ), ], From 45e22aa73d0e44c96d8e9b34fab9cabc941e0567 Mon Sep 17 00:00:00 2001 From: Andre Rossi Date: Tue, 28 Nov 2023 10:43:22 -0300 Subject: [PATCH 12/22] feat: improve code readability --- .../chat/ds_reply_container.widget.dart | 160 +++++++++--------- 1 file changed, 77 insertions(+), 83 deletions(-) diff --git a/lib/src/widgets/chat/ds_reply_container.widget.dart b/lib/src/widgets/chat/ds_reply_container.widget.dart index 83199263..1c969039 100644 --- a/lib/src/widgets/chat/ds_reply_container.widget.dart +++ b/lib/src/widgets/chat/ds_reply_container.widget.dart @@ -20,116 +20,110 @@ class DSReplyContainer extends StatelessWidget { final dynamic replyContent; final DSMessageBubbleStyle style; + get _foregroundColor => style.isLightBubbleBackground(align) + ? DSColors.contentDefault + : DSColors.surface1; + @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.all( - 8.0, - ), + padding: const EdgeInsets.all(8.0), child: Column( children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Icon( - DSIcons.undo_outline, - color: style.isLightBubbleBackground(align) - ? DSColors.neutralDarkCity - : DSColors.neutralLightSnow, - ), - const SizedBox(width: 8.0), - DSCaptionText( - 'Reply', - fontStyle: FontStyle.italic, - color: style.isLightBubbleBackground(align) - ? DSColors.neutralDarkCity - : DSColors.neutralLightSnow, - ), - ], - ), - const SizedBox(height: 4.0), - DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4.0), - border: Border.all( - color: style.isLightBubbleBackground(align) - ? DSColors.contentGhost - : DSColors.contentDisable, - ), + _buildTitle(), + _buildReplyContainer(), + ], + ), + ); + } + + Widget _buildTitle() => Padding( + padding: const EdgeInsets.only(bottom: 4.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + DSIcons.undo_outline, color: style.isLightBubbleBackground(align) - ? DSColors.surface3 - : DSColors.contentDefault, + ? DSColors.neutralDarkCity + : DSColors.neutralLightSnow, ), - child: IntrinsicHeight( - child: Row( - children: [ - Container( - decoration: const ShapeDecoration( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(8.0), - bottomLeft: Radius.circular(8.0), - ), - ), - color: DSColors.primary, - ), - width: 4.0, - ), - Flexible( - child: Padding( - padding: const EdgeInsets.only( - top: 8.0, - left: 8.0, - bottom: 8.0, - ), - child: _replyWidget(replyContent, style, align), + const SizedBox(width: 8.0), + DSCaptionText( + 'Reply', + fontStyle: FontStyle.italic, + color: style.isLightBubbleBackground(align) + ? DSColors.neutralDarkCity + : DSColors.neutralLightSnow, + ), + ], + ), + ); + + Widget _buildReplyContainer() => DecoratedBox( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4.0), + border: Border.all( + color: style.isLightBubbleBackground(align) + ? DSColors.contentGhost + : DSColors.contentDisable, + ), + color: style.isLightBubbleBackground(align) + ? DSColors.surface3 + : DSColors.contentDefault, + ), + child: IntrinsicHeight( + child: Row( + children: [ + Container( + decoration: const ShapeDecoration( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(8.0), + bottomLeft: Radius.circular(8.0), ), ), - ], + color: DSColors.primary, + ), + width: 4.0, ), - ), + Flexible( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: _buildReply(), + ), + ), + ], ), - ], - ), - ); - } -} + ), + ); + + Widget _buildReply() => switch (replyContent['type']) { + DSMessageContentType.textPlain => _buildTextPlain(), + _ => _buildDefault(), + }; -Widget _replyWidget( - dynamic replyContent, - DSMessageBubbleStyle style, - DSAlign align, -) { - switch (replyContent['type']) { - case DSMessageContentType.textPlain: - return DSBodyText( + Widget _buildTextPlain() => DSBodyText( replyContent['value'] is String ? replyContent['value'] : '**********', - color: _color(align, style), + color: _foregroundColor, overflow: TextOverflow.visible, ); - default: - return Row( + + Widget _buildDefault() => Row( mainAxisSize: MainAxisSize.max, children: [ Icon( DSIcons.warning_outline, - color: _color(align, style), + color: _foregroundColor, ), const SizedBox(width: 8.0), Flexible( child: DSBodyText( 'Failed to load message', overflow: TextOverflow.visible, - color: _color(align, style), + color: _foregroundColor, ), ), ], ); - } -} - -Color _color(DSAlign align, DSMessageBubbleStyle style) { - return style.isLightBubbleBackground(align) - ? DSColors.contentDefault - : DSColors.surface1; } From 076d438f77ddc562184b195a8bcd503f6e8e9dfb Mon Sep 17 00:00:00 2001 From: Andre Rossi Date: Tue, 28 Nov 2023 16:26:10 -0300 Subject: [PATCH 13/22] refactor: altered to use DSUtils.bubbleMinSize --- .../chat/ds_image_message_bubble.widget.dart | 7 ++++--- .../chat/ds_location_message_bubble.widget.dart | 5 +++-- .../chat/video/ds_video_message_bubble.widget.dart | 13 +++++++------ 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/src/widgets/chat/ds_image_message_bubble.widget.dart b/lib/src/widgets/chat/ds_image_message_bubble.widget.dart index e3ba97db..67533753 100644 --- a/lib/src/widgets/chat/ds_image_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_image_message_bubble.widget.dart @@ -7,6 +7,7 @@ import '../../enums/ds_border_radius.enum.dart'; import '../../models/ds_document_select.model.dart'; import '../../models/ds_message_bubble_style.model.dart'; import '../../themes/colors/ds_colors.theme.dart'; +import '../../utils/ds_utils.util.dart'; import '../texts/ds_caption_text.widget.dart'; import '../utils/ds_circular_progress.widget.dart'; import '../utils/ds_expanded_image.widget.dart'; @@ -80,7 +81,7 @@ class _DSImageMessageBubbleState extends State : DSColors.neutralLightSnow; return DSMessageBubble( - defaultMaxSize: 240.0, + defaultMaxSize: DSUtils.bubbleMinSize, shouldUseDefaultSize: true, align: widget.align, borderRadius: widget.borderRadius, @@ -93,12 +94,12 @@ class _DSImageMessageBubbleState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( - width: 240, + width: DSUtils.bubbleMinSize, height: 200, child: Obx( () => _controller.localPath.value != null ? DSExpandedImage( - width: 240.0, + width: DSUtils.bubbleMinSize, appBarText: widget.appBarText, appBarPhotoUri: widget.appBarPhotoUri, url: _controller.localPath.value!, diff --git a/lib/src/widgets/chat/ds_location_message_bubble.widget.dart b/lib/src/widgets/chat/ds_location_message_bubble.widget.dart index a838ca8c..aa94479d 100644 --- a/lib/src/widgets/chat/ds_location_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_location_message_bubble.widget.dart @@ -7,6 +7,7 @@ import '../../models/ds_message_bubble_style.model.dart'; import '../../services/ds_auth.service.dart'; import '../../themes/colors/ds_colors.theme.dart'; import '../../themes/icons/ds_icons.dart'; +import '../../utils/ds_utils.util.dart'; import '../animations/ds_spinner_loading.widget.dart'; import '../texts/ds_body_text.widget.dart'; import '../utils/ds_cached_network_image_view.widget.dart'; @@ -53,8 +54,8 @@ class DSLocationMessageBubble extends StatelessWidget { }, child: DSMessageBubble( shouldUseDefaultSize: true, - defaultMaxSize: 240.0, - defaultMinSize: 240.0, + defaultMaxSize: DSUtils.bubbleMinSize, + defaultMinSize: DSUtils.bubbleMinSize, borderRadius: borderRadius, padding: EdgeInsets.zero, align: align, diff --git a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart index 6e3b1485..41ed102c 100644 --- a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart +++ b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart @@ -10,6 +10,7 @@ import '../../../models/ds_message_bubble_style.model.dart'; import '../../../services/ds_auth.service.dart'; import '../../../themes/colors/ds_colors.theme.dart'; import '../../../themes/icons/ds_icons.dart'; +import '../../../utils/ds_utils.util.dart'; import '../../animations/ds_uploading.widget.dart'; import '../../buttons/ds_button.widget.dart'; import '../../utils/ds_circular_progress.widget.dart'; @@ -132,8 +133,8 @@ class _DSVideoMessageBubbleState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( - height: 240, - width: 240, + height: DSUtils.bubbleMinSize, + width: DSUtils.bubbleMinSize, child: widget.isUploading ? Stack( children: [ @@ -142,8 +143,8 @@ class _DSVideoMessageBubbleState extends State widget.url, ), opacity: const AlwaysStoppedAnimation(.3), - width: 240, - height: 240, + width: DSUtils.bubbleMinSize, + height: DSUtils.bubbleMinSize, fit: BoxFit.cover, ), const Positioned( @@ -212,8 +213,8 @@ class _DSVideoMessageBubbleState extends State File( _controller.thumbnail.value, ), - width: 240, - height: 240, + width: DSUtils.bubbleMinSize, + height: DSUtils.bubbleMinSize, fit: BoxFit.cover, ), ), From 6fcac755392d0e1e245a4232f43d931998df5a0e Mon Sep 17 00:00:00 2001 From: Andre Rossi Date: Tue, 28 Nov 2023 16:30:31 -0300 Subject: [PATCH 14/22] refactor: set default values in constructor --- lib/src/widgets/animations/ds_uploading.widget.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/widgets/animations/ds_uploading.widget.dart b/lib/src/widgets/animations/ds_uploading.widget.dart index ad41888f..dc9a9b83 100644 --- a/lib/src/widgets/animations/ds_uploading.widget.dart +++ b/lib/src/widgets/animations/ds_uploading.widget.dart @@ -7,13 +7,13 @@ import '../../themes/colors/ds_colors.theme.dart'; import '../../themes/icons/ds_icons.dart'; class DSUploading extends StatefulWidget { - final double? size; - final Color? color; + final double size; + final Color color; const DSUploading({ super.key, - this.size, - this.color, + this.size = 24.0, + this.color = DSColors.neutralLightSnow, }); @override @@ -43,8 +43,8 @@ class _DSUploadingState extends State { duration: const Duration(seconds: 1), child: Icon( DSIcons.upload_outline, - color: widget.color ?? DSColors.neutralLightSnow, - size: widget.size ?? 24.0, + color: widget.color, + size: widget.size, ), ), ), From faf139bc13555240c646037db1d3e647915f4734 Mon Sep 17 00:00:00 2001 From: Andre Rossi Date: Tue, 28 Nov 2023 16:40:55 -0300 Subject: [PATCH 15/22] refactor: altered to use an icon --- .../chat/video/ds_video_message_bubble.widget.dart | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart index 41ed102c..fce3559e 100644 --- a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart +++ b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart @@ -164,11 +164,7 @@ class _DSVideoMessageBubbleState extends State : _isInitialized ? Obx( () => _controller.hasError.value - ? const Icon( - DSIcons.video_broken_outline, - size: 80.0, - color: DSColors.neutralDarkRooftop, - ) + ? _buidErrorIcon() : _controller.isLoadingThumbnail.value ? const SizedBox.shrink() : _controller.isDownloading.value @@ -220,7 +216,7 @@ class _DSVideoMessageBubbleState extends State ), ), ) - : const SizedBox.shrink(), + : _buidErrorIcon(), ), if (widget.text?.isNotEmpty ?? false) Padding( @@ -240,4 +236,10 @@ class _DSVideoMessageBubbleState extends State ), ); } + + Widget _buidErrorIcon() => const Icon( + DSIcons.video_broken_outline, + size: 80.0, + color: DSColors.neutralDarkRooftop, + ); } From 398feef70f91213e3d20d45156eafa7242a2e3a2 Mon Sep 17 00:00:00 2001 From: Marcelo Amaro Date: Tue, 28 Nov 2023 17:07:45 -0300 Subject: [PATCH 16/22] test: Update Goldens --- .../ds_delivery_report_icon.png | Bin 38784 -> 38684 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/widgets/chat/goldens/ds_delivery_report_icon/ds_delivery_report_icon.png b/test/widgets/chat/goldens/ds_delivery_report_icon/ds_delivery_report_icon.png index 30441168b4ae0c31f0153ff4ecaffcdb0774e5c2..4776337deebe12a9c28f322ac5ac46c34423ecb8 100644 GIT binary patch delta 20170 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 nCnqO{`rcaS$M*cM-AlCg+v( Date: Tue, 28 Nov 2023 18:16:16 -0300 Subject: [PATCH 17/22] fix: set size to bubble --- .../video/ds_video_message_bubble.widget.dart | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart index 2e565598..153f445d 100644 --- a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart +++ b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart @@ -128,6 +128,8 @@ class _DSVideoMessageBubbleState extends State : DSColors.neutralDarkCity; return DSMessageBubble( + defaultMaxSize: DSUtils.bubbleMinSize, + shouldUseDefaultSize: true, replyContent: widget.replyContent, align: widget.align, borderRadius: widget.borderRadius, @@ -224,16 +226,19 @@ class _DSVideoMessageBubbleState extends State : _buidErrorIcon(), ), if (widget.text?.isNotEmpty ?? false) - Padding( - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, - ), - child: DSShowMoreText( - text: widget.text!, - align: widget.align, - style: widget.style, - maxWidth: constraints.maxWidth, + SizedBox( + width: DSUtils.bubbleMinSize, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8.0, + horizontal: 16.0, + ), + child: DSShowMoreText( + text: widget.text!, + align: widget.align, + style: widget.style, + maxWidth: constraints.maxWidth, + ), ), ), ], From ad1ea877e2d8ed835afe6c515d2a1342db076ea8 Mon Sep 17 00:00:00 2001 From: Andre Rossi Date: Wed, 29 Nov 2023 15:39:36 -0300 Subject: [PATCH 18/22] refactor: general improvements --- lib/blip_ds.dart | 3 +- lib/src/models/ds_reply_content.model.dart | 47 ++++++++++++++++ .../audio/ds_audio_message_bubble.widget.dart | 3 +- .../ds_contact_message_bubble.widget.dart | 3 +- .../chat/ds_file_message_bubble.widget.dart | 3 +- .../chat/ds_image_message_bubble.widget.dart | 3 +- .../ds_location_message_bubble.widget.dart | 3 +- .../chat/ds_message_bubble.widget.dart | 3 +- .../chat/ds_reply_container.widget.dart | 53 +++++++++++++++---- .../ds_request_location_bubble.widget.dart | 3 +- .../chat/ds_text_message_bubble.widget.dart | 3 +- ...pported_content_message_bubble.widget.dart | 3 +- ... => ds_weblink_message_bubble.widget.dart} | 7 +-- .../video/ds_video_message_bubble.widget.dart | 3 +- lib/src/widgets/utils/ds_card.widget.dart | 15 +++--- .../showcase/sample_weblink.showcase.dart | 4 +- 16 files changed, 127 insertions(+), 32 deletions(-) create mode 100644 lib/src/models/ds_reply_content.model.dart rename lib/src/widgets/chat/{ds_weblink.widget.dart => ds_weblink_message_bubble.widget.dart} (93%) diff --git a/lib/blip_ds.dart b/lib/blip_ds.dart index db10c997..5378d309 100644 --- a/lib/blip_ds.dart +++ b/lib/blip_ds.dart @@ -144,7 +144,8 @@ export 'src/widgets/chat/ds_text_message_bubble.widget.dart' export 'src/widgets/chat/ds_unsupported_content_message_bubble.widget.dart' show DSUnsupportedContentMessageBubble; export 'src/widgets/chat/ds_url_preview.widget.dart' show DSUrlPreview; -export 'src/widgets/chat/ds_weblink.widget.dart' show DSWeblink; +export 'src/widgets/chat/ds_weblink_message_bubble.widget.dart' + show DSWeblinkMessageBubble; export 'src/widgets/chat/typing/ds_typing_dot_animation.widget.dart' show DSTypingDotAnimation; export 'src/widgets/chat/typing/ds_typing_message_bubble.widget.dart' diff --git a/lib/src/models/ds_reply_content.model.dart b/lib/src/models/ds_reply_content.model.dart new file mode 100644 index 00000000..efb736c0 --- /dev/null +++ b/lib/src/models/ds_reply_content.model.dart @@ -0,0 +1,47 @@ +class DSReplyContent { + DSReplyContentReplied replied; + DSReplyContentInReplyTo inReplyTo; + + DSReplyContent({ + required this.replied, + required this.inReplyTo, + }); + + DSReplyContent.fromJson(Map json) + : replied = DSReplyContentReplied.fromJson(json['replied']), + inReplyTo = DSReplyContentInReplyTo.fromJson(json['inReplyTo']); +} + +class DSReplyContentInReplyTo { + String id; + String type; + dynamic value; + String direction; + + DSReplyContentInReplyTo({ + required this.id, + required this.type, + required this.value, + required this.direction, + }); + + DSReplyContentInReplyTo.fromJson(Map json) + : id = json['id'], + type = json['type'], + value = json['value'], + direction = json['direction']; +} + +class DSReplyContentReplied { + String type; + dynamic value; + + DSReplyContentReplied({ + required this.type, + required this.value, + }); + + DSReplyContentReplied.fromJson(Map json) + : type = json['type'], + value = json['value']; +} diff --git a/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart b/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart index 7d5bd194..7c16bdba 100644 --- a/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart +++ b/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import '../../../enums/ds_align.enum.dart'; import '../../../enums/ds_border_radius.enum.dart'; import '../../../models/ds_message_bubble_style.model.dart'; +import '../../../models/ds_reply_content.model.dart'; import '../../../themes/colors/ds_colors.theme.dart'; import '../ds_message_bubble.widget.dart'; import 'ds_audio_player.widget.dart'; @@ -14,7 +15,7 @@ class DSAudioMessageBubble extends StatelessWidget { final DSMessageBubbleStyle style; final String? uniqueId; final bool shouldAuthenticate; - final dynamic replyContent; + final DSReplyContent? replyContent; DSAudioMessageBubble({ super.key, diff --git a/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart b/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart index 1c7f930e..abebb950 100644 --- a/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import '../../enums/ds_align.enum.dart'; import '../../enums/ds_border_radius.enum.dart'; import '../../models/ds_message_bubble_style.model.dart'; +import '../../models/ds_reply_content.model.dart'; import '../../themes/colors/ds_colors.theme.dart'; import '../../themes/texts/utils/ds_font_weights.theme.dart'; import '../texts/ds_body_text.widget.dart'; @@ -14,7 +15,7 @@ class DSContactMessageBubble extends StatelessWidget { final String? phone; final String? email; final String? address; - final dynamic replyContent; + final DSReplyContent? replyContent; final DSAlign align; final List borderRadius; final DSMessageBubbleStyle style; diff --git a/lib/src/widgets/chat/ds_file_message_bubble.widget.dart b/lib/src/widgets/chat/ds_file_message_bubble.widget.dart index 89874e79..1e5e046d 100644 --- a/lib/src/widgets/chat/ds_file_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_file_message_bubble.widget.dart @@ -5,6 +5,7 @@ import '../../controllers/chat/ds_file_message_bubble.controller.dart'; import '../../enums/ds_align.enum.dart'; import '../../enums/ds_border_radius.enum.dart'; import '../../models/ds_message_bubble_style.model.dart'; +import '../../models/ds_reply_content.model.dart'; import '../../services/ds_auth.service.dart'; import '../../themes/colors/ds_colors.theme.dart'; import '../animations/ds_fading_circle_loading.widget.dart'; @@ -24,7 +25,7 @@ class DSFileMessageBubble extends StatelessWidget { final DSMessageBubbleStyle style; final bool shouldAuthenticate; final bool isUploading; - final dynamic replyContent; + final DSReplyContent? replyContent; /// Creates a Design System's [DSMessageBubble] used on files other than image, audio, or video DSFileMessageBubble({ diff --git a/lib/src/widgets/chat/ds_image_message_bubble.widget.dart b/lib/src/widgets/chat/ds_image_message_bubble.widget.dart index 5455baee..1f7bf9b3 100644 --- a/lib/src/widgets/chat/ds_image_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_image_message_bubble.widget.dart @@ -6,6 +6,7 @@ import '../../enums/ds_align.enum.dart'; import '../../enums/ds_border_radius.enum.dart'; import '../../models/ds_document_select.model.dart'; import '../../models/ds_message_bubble_style.model.dart'; +import '../../models/ds_reply_content.model.dart'; import '../../themes/colors/ds_colors.theme.dart'; import '../../utils/ds_utils.util.dart'; import '../texts/ds_caption_text.widget.dart'; @@ -53,7 +54,7 @@ class DSImageMessageBubble extends StatefulWidget { final bool shouldAuthenticate; final String? mediaType; final bool isUploading; - final dynamic replyContent; + final DSReplyContent? replyContent; @override State createState() => _DSImageMessageBubbleState(); diff --git a/lib/src/widgets/chat/ds_location_message_bubble.widget.dart b/lib/src/widgets/chat/ds_location_message_bubble.widget.dart index 1a1b897f..b4616fcf 100644 --- a/lib/src/widgets/chat/ds_location_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_location_message_bubble.widget.dart @@ -4,6 +4,7 @@ import 'package:map_launcher/map_launcher.dart'; import '../../enums/ds_align.enum.dart'; import '../../enums/ds_border_radius.enum.dart'; import '../../models/ds_message_bubble_style.model.dart'; +import '../../models/ds_reply_content.model.dart'; import '../../services/ds_auth.service.dart'; import '../../themes/colors/ds_colors.theme.dart'; import '../../themes/icons/ds_icons.dart'; @@ -17,7 +18,7 @@ class DSLocationMessageBubble extends StatelessWidget { final DSAlign align; final DSMessageBubbleStyle style; final String? title; - final dynamic replyContent; + final DSReplyContent? replyContent; final String latitude; final String longitude; final List borderRadius; diff --git a/lib/src/widgets/chat/ds_message_bubble.widget.dart b/lib/src/widgets/chat/ds_message_bubble.widget.dart index c977fa6e..afa3bb07 100644 --- a/lib/src/widgets/chat/ds_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_message_bubble.widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import '../../enums/ds_align.enum.dart'; import '../../enums/ds_border_radius.enum.dart'; import '../../models/ds_message_bubble_style.model.dart'; +import '../../models/ds_reply_content.model.dart'; import '../../utils/ds_bubble.util.dart'; import '../../utils/ds_utils.util.dart'; import '../animations/ds_animated_size.widget.dart'; @@ -11,7 +12,7 @@ import 'ds_reply_container.widget.dart'; class DSMessageBubble extends StatelessWidget { final DSAlign align; final Widget child; - final dynamic replyContent; + final DSReplyContent? replyContent; final List borderRadius; final EdgeInsets padding; final bool shouldUseDefaultSize; diff --git a/lib/src/widgets/chat/ds_reply_container.widget.dart b/lib/src/widgets/chat/ds_reply_container.widget.dart index 1c969039..67332dcc 100644 --- a/lib/src/widgets/chat/ds_reply_container.widget.dart +++ b/lib/src/widgets/chat/ds_reply_container.widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../enums/ds_align.enum.dart'; import '../../models/ds_message_bubble_style.model.dart'; +import '../../models/ds_reply_content.model.dart'; import '../../themes/colors/ds_colors.theme.dart'; import '../../themes/icons/ds_icons.dart'; import '../../utils/ds_message_content_type.util.dart'; @@ -9,19 +10,19 @@ import '../texts/ds_body_text.widget.dart'; import '../texts/ds_caption_text.widget.dart'; class DSReplyContainer extends StatelessWidget { - const DSReplyContainer({ + DSReplyContainer({ super.key, required this.replyContent, - required this.style, required this.align, - }); + DSMessageBubbleStyle? style, + }) : style = style ?? DSMessageBubbleStyle(); final DSAlign align; - final dynamic replyContent; + final DSReplyContent replyContent; final DSMessageBubbleStyle style; - get _foregroundColor => style.isLightBubbleBackground(align) - ? DSColors.contentDefault + Color get _foregroundColor => style.isLightBubbleBackground(align) + ? const Color.fromARGB(255, 39, 4, 4) : DSColors.surface1; @override @@ -47,6 +48,7 @@ class DSReplyContainer extends StatelessWidget { color: style.isLightBubbleBackground(align) ? DSColors.neutralDarkCity : DSColors.neutralLightSnow, + size: 24.0, ), const SizedBox(width: 8.0), DSCaptionText( @@ -98,23 +100,54 @@ class DSReplyContainer extends StatelessWidget { ), ); - Widget _buildReply() => switch (replyContent['type']) { + Widget _buildReply() => switch (replyContent.inReplyTo.type) { DSMessageContentType.textPlain => _buildTextPlain(), + DSMessageContentType.applicationJson => _buildApplicationJson(), + DSMessageContentType.select => _buidSelect(), _ => _buildDefault(), }; - Widget _buildTextPlain() => DSBodyText( - replyContent['value'] is String ? replyContent['value'] : '**********', + Widget _buidSelect() => DSBodyText( + replyContent.inReplyTo.value['text'], color: _foregroundColor, overflow: TextOverflow.visible, ); + Widget _buildApplicationJson() => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (replyContent.inReplyTo.value['interactive']['body']?['text'] != + null) + DSBodyText( + replyContent.inReplyTo.value['interactive']['body']?['text'], + color: _foregroundColor, + overflow: TextOverflow.visible, + ), + if (replyContent.inReplyTo.value['interactive']['footer']?['text'] != + null) + DSCaptionText( + replyContent.inReplyTo.value['interactive']['footer']?['text'], + fontStyle: FontStyle.italic, + overflow: TextOverflow.visible, + color: _foregroundColor, + ), + ], + ); + + Widget _buildTextPlain() => replyContent.inReplyTo.value is String + ? DSBodyText( + replyContent.inReplyTo.value, + color: _foregroundColor, + overflow: TextOverflow.visible, + ) + : _buildDefault(); + Widget _buildDefault() => Row( - mainAxisSize: MainAxisSize.max, children: [ Icon( DSIcons.warning_outline, color: _foregroundColor, + size: 24.0, ), const SizedBox(width: 8.0), Flexible( diff --git a/lib/src/widgets/chat/ds_request_location_bubble.widget.dart b/lib/src/widgets/chat/ds_request_location_bubble.widget.dart index b43455d6..c170bc60 100644 --- a/lib/src/widgets/chat/ds_request_location_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_request_location_bubble.widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import '../../enums/ds_align.enum.dart'; import '../../enums/ds_border_radius.enum.dart'; import '../../models/ds_message_bubble_style.model.dart'; +import '../../models/ds_reply_content.model.dart'; import '../../utils/ds_bubble.util.dart'; import '../../utils/ds_message_content_type.util.dart'; import '../buttons/ds_request_location_button.widget.dart'; @@ -24,7 +25,7 @@ class DSRequestLocationBubble extends StatelessWidget { final String? label; final String type; final String? value; - final dynamic replyContent; + final DSReplyContent? replyContent; final DSAlign align; final List borderRadius; final bool showRequestLocationButton; diff --git a/lib/src/widgets/chat/ds_text_message_bubble.widget.dart b/lib/src/widgets/chat/ds_text_message_bubble.widget.dart index a1c5c98b..fc81bc89 100644 --- a/lib/src/widgets/chat/ds_text_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_text_message_bubble.widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import '../../enums/ds_align.enum.dart'; import '../../enums/ds_border_radius.enum.dart'; import '../../models/ds_message_bubble_style.model.dart'; +import '../../models/ds_reply_content.model.dart'; import '../../themes/colors/ds_colors.theme.dart'; import '../../utils/ds_linkify.util.dart'; import 'ds_message_bubble.widget.dart'; @@ -16,7 +17,7 @@ class DSTextMessageBubble extends StatefulWidget { final bool hasSpacer; final List borderRadius; final dynamic selectContent; - final dynamic replyContent; + final DSReplyContent? replyContent; final bool showSelect; final void Function(String, Map)? onSelected; final DSMessageBubbleStyle style; diff --git a/lib/src/widgets/chat/ds_unsupported_content_message_bubble.widget.dart b/lib/src/widgets/chat/ds_unsupported_content_message_bubble.widget.dart index 160b4d5f..bb7732b9 100644 --- a/lib/src/widgets/chat/ds_unsupported_content_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_unsupported_content_message_bubble.widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import '../../enums/ds_align.enum.dart'; import '../../enums/ds_border_radius.enum.dart'; import '../../models/ds_message_bubble_style.model.dart'; +import '../../models/ds_reply_content.model.dart'; import '../../themes/colors/ds_colors.theme.dart'; import '../../themes/icons/ds_icons.dart'; import '../texts/ds_body_text.widget.dart'; @@ -11,7 +12,7 @@ import 'ds_message_bubble.widget.dart'; class DSUnsupportedContentMessageBubble extends StatelessWidget { final DSAlign align; final Widget? leftWidget; - final dynamic replyContent; + final DSReplyContent? replyContent; final String? text; final TextOverflow overflow; final List borderRadius; diff --git a/lib/src/widgets/chat/ds_weblink.widget.dart b/lib/src/widgets/chat/ds_weblink_message_bubble.widget.dart similarity index 93% rename from lib/src/widgets/chat/ds_weblink.widget.dart rename to lib/src/widgets/chat/ds_weblink_message_bubble.widget.dart index 1bcb3953..c67f3e34 100644 --- a/lib/src/widgets/chat/ds_weblink.widget.dart +++ b/lib/src/widgets/chat/ds_weblink_message_bubble.widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import '../../enums/ds_align.enum.dart'; import '../../enums/ds_border_radius.enum.dart'; import '../../models/ds_message_bubble_style.model.dart'; +import '../../models/ds_reply_content.model.dart'; import '../../themes/colors/ds_colors.theme.dart'; import '../texts/ds_body_text.widget.dart'; import '../texts/ds_headline_small_text.widget.dart'; @@ -10,7 +11,7 @@ import 'ds_message_bubble.widget.dart'; /// A Design System widget used to display weblinks. -class DSWeblink extends StatelessWidget { +class DSWeblinkMessageBubble extends StatelessWidget { /// Show the [title] on the card final String title; @@ -30,9 +31,9 @@ class DSWeblink extends StatelessWidget { final DSMessageBubbleStyle style; /// replyContent - final dynamic replyContent; + final DSReplyContent? replyContent; - DSWeblink({ + DSWeblinkMessageBubble({ Key? key, required this.title, required this.text, diff --git a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart index 153f445d..d8bed8c7 100644 --- a/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart +++ b/lib/src/widgets/chat/video/ds_video_message_bubble.widget.dart @@ -7,6 +7,7 @@ import '../../../controllers/chat/ds_video_message_bubble.controller.dart'; import '../../../enums/ds_align.enum.dart'; import '../../../enums/ds_border_radius.enum.dart'; import '../../../models/ds_message_bubble_style.model.dart'; +import '../../../models/ds_reply_content.model.dart'; import '../../../services/ds_auth.service.dart'; import '../../../themes/colors/ds_colors.theme.dart'; import '../../../themes/icons/ds_icons.dart'; @@ -54,7 +55,7 @@ class DSVideoMessageBubble extends StatefulWidget { final bool isUploading; // reply id message - final dynamic replyContent; + final DSReplyContent? replyContent; /// Card for the purpose of triggering a video to play. /// diff --git a/lib/src/widgets/utils/ds_card.widget.dart b/lib/src/widgets/utils/ds_card.widget.dart index a776a245..6f05e57a 100644 --- a/lib/src/widgets/utils/ds_card.widget.dart +++ b/lib/src/widgets/utils/ds_card.widget.dart @@ -10,6 +10,7 @@ import '../../models/ds_document_select.model.dart'; import '../../models/ds_media_link.model.dart'; import '../../models/ds_message_bubble_avatar_config.model.dart'; import '../../models/ds_message_bubble_style.model.dart'; +import '../../models/ds_reply_content.model.dart'; import '../../services/ds_file.service.dart'; import '../../utils/ds_message_content_type.util.dart'; import '../chat/audio/ds_audio_message_bubble.widget.dart'; @@ -23,7 +24,7 @@ import '../chat/ds_quick_reply.widget.dart'; import '../chat/ds_request_location_bubble.widget.dart'; import '../chat/ds_text_message_bubble.widget.dart'; import '../chat/ds_unsupported_content_message_bubble.widget.dart'; -import '../chat/ds_weblink.widget.dart'; +import '../chat/ds_weblink_message_bubble.widget.dart'; import '../chat/video/ds_video_message_bubble.widget.dart'; import '../ticket_message/ds_ticket_message.widget.dart'; @@ -62,7 +63,7 @@ class DSCard extends StatelessWidget { final Map? customer; final bool showQuickReplyOptions; final bool showRequestLocationButton; - final dynamic replyContent; + final DSReplyContent? replyContent; final bool isUploading; @override @@ -86,9 +87,11 @@ class DSCard extends StatelessWidget { return _buildContact(); case DSMessageContentType.reply: + final replyContent = DSReplyContent.fromJson(content); + return DSCard( - type: content['replied']['type'], - content: content['replied']['value'], + type: replyContent.replied.type, + content: replyContent.replied.value, align: align, borderRadius: borderRadius, status: status, @@ -98,7 +101,7 @@ class DSCard extends StatelessWidget { onOpenLink: onOpenLink, messageId: messageId, customer: customer, - replyContent: content['inReplyTo'], + replyContent: replyContent, ); case DSMessageContentType.mediaLink: @@ -122,7 +125,7 @@ class DSCard extends StatelessWidget { ); case DSMessageContentType.webLink: - return DSWeblink( + return DSWeblinkMessageBubble( title: content['title'], text: content['text'], url: content['uri'], diff --git a/sample/lib/widgets/showcase/sample_weblink.showcase.dart b/sample/lib/widgets/showcase/sample_weblink.showcase.dart index 744cf769..e9a14517 100644 --- a/sample/lib/widgets/showcase/sample_weblink.showcase.dart +++ b/sample/lib/widgets/showcase/sample_weblink.showcase.dart @@ -8,14 +8,14 @@ class SampleWeblinkShowcase extends StatelessWidget { Widget build(BuildContext context) { return Column( children: [ - DSWeblink( + DSWeblinkMessageBubble( title: 'Take Blip', text: 'Atenda de forma inteligente, no canal digital que seu cliente prefere', url: 'https://www.take.net/', align: DSAlign.right, ), - DSWeblink( + DSWeblinkMessageBubble( title: 'Take Blip', text: 'Atenda de forma inteligente, no canal digital que seu cliente prefere', From 2fd725ec0be67d584b25516ddd48acbb47ac4891 Mon Sep 17 00:00:00 2001 From: Andre Rossi Date: Wed, 29 Nov 2023 16:10:05 -0300 Subject: [PATCH 19/22] feat: added samples --- lib/blip_ds.dart | 6 +++ lib/src/models/ds_reply_content.model.dart | 37 ++------------ .../ds_reply_content_in_reply_to.model.dart | 19 +++++++ .../ds_reply_content_replied.model.dart | 13 +++++ .../sample_message_bubble.showcase.dart | 51 +++++++++++++++++++ 5 files changed, 92 insertions(+), 34 deletions(-) create mode 100644 lib/src/models/ds_reply_content_in_reply_to.model.dart create mode 100644 lib/src/models/ds_reply_content_replied.model.dart diff --git a/lib/blip_ds.dart b/lib/blip_ds.dart index 5378d309..d783b8cf 100644 --- a/lib/blip_ds.dart +++ b/lib/blip_ds.dart @@ -21,6 +21,11 @@ export 'src/models/ds_message_bubble_avatar_config.model.dart' export 'src/models/ds_message_bubble_style.model.dart' show DSMessageBubbleStyle; export 'src/models/ds_message_item.model.dart' show DSMessageItem; +export 'src/models/ds_reply_content.model.dart' show DSReplyContent; +export 'src/models/ds_reply_content_in_reply_to.model.dart' + show DSReplyContentInReplyTo; +export 'src/models/ds_reply_content_replied.model.dart' + show DSReplyContentReplied; export 'src/models/ds_toast_props.model.dart' show DSToastProps; export 'src/models/interactive_message/ds_interactive_message.model.dart' show DSInteractiveMessage; @@ -135,6 +140,7 @@ export 'src/widgets/chat/ds_location_message_bubble.widget.dart' export 'src/widgets/chat/ds_message_bubble.widget.dart' show DSMessageBubble; export 'src/widgets/chat/ds_message_bubble_detail.widget.dart' show DSMessageBubbleDetail; +export 'src/widgets/chat/ds_reply_container.widget.dart' show DSReplyContainer; export 'src/widgets/chat/ds_request_location_bubble.widget.dart' show DSRequestLocationBubble; export 'src/widgets/chat/ds_survey_message_bubble.widget.dart' diff --git a/lib/src/models/ds_reply_content.model.dart b/lib/src/models/ds_reply_content.model.dart index efb736c0..f1e519c2 100644 --- a/lib/src/models/ds_reply_content.model.dart +++ b/lib/src/models/ds_reply_content.model.dart @@ -1,3 +1,6 @@ +import 'ds_reply_content_in_reply_to.model.dart'; +import 'ds_reply_content_replied.model.dart'; + class DSReplyContent { DSReplyContentReplied replied; DSReplyContentInReplyTo inReplyTo; @@ -11,37 +14,3 @@ class DSReplyContent { : replied = DSReplyContentReplied.fromJson(json['replied']), inReplyTo = DSReplyContentInReplyTo.fromJson(json['inReplyTo']); } - -class DSReplyContentInReplyTo { - String id; - String type; - dynamic value; - String direction; - - DSReplyContentInReplyTo({ - required this.id, - required this.type, - required this.value, - required this.direction, - }); - - DSReplyContentInReplyTo.fromJson(Map json) - : id = json['id'], - type = json['type'], - value = json['value'], - direction = json['direction']; -} - -class DSReplyContentReplied { - String type; - dynamic value; - - DSReplyContentReplied({ - required this.type, - required this.value, - }); - - DSReplyContentReplied.fromJson(Map json) - : type = json['type'], - value = json['value']; -} diff --git a/lib/src/models/ds_reply_content_in_reply_to.model.dart b/lib/src/models/ds_reply_content_in_reply_to.model.dart new file mode 100644 index 00000000..cb213675 --- /dev/null +++ b/lib/src/models/ds_reply_content_in_reply_to.model.dart @@ -0,0 +1,19 @@ +class DSReplyContentInReplyTo { + String id; + String type; + dynamic value; + String direction; + + DSReplyContentInReplyTo({ + required this.id, + required this.type, + required this.value, + required this.direction, + }); + + DSReplyContentInReplyTo.fromJson(Map json) + : id = json['id'], + type = json['type'], + value = json['value'], + direction = json['direction']; +} diff --git a/lib/src/models/ds_reply_content_replied.model.dart b/lib/src/models/ds_reply_content_replied.model.dart new file mode 100644 index 00000000..6b2d2bcf --- /dev/null +++ b/lib/src/models/ds_reply_content_replied.model.dart @@ -0,0 +1,13 @@ +class DSReplyContentReplied { + String type; + dynamic value; + + DSReplyContentReplied({ + required this.type, + required this.value, + }); + + DSReplyContentReplied.fromJson(Map json) + : type = json['type'], + value = json['value']; +} diff --git a/sample/lib/widgets/showcase/sample_message_bubble.showcase.dart b/sample/lib/widgets/showcase/sample_message_bubble.showcase.dart index c2f92c86..07d10eb7 100644 --- a/sample/lib/widgets/showcase/sample_message_bubble.showcase.dart +++ b/sample/lib/widgets/showcase/sample_message_bubble.showcase.dart @@ -53,6 +53,57 @@ class SampleMessageBubbleShowcase extends StatelessWidget { return Obx( () => Column( children: [ + DSTextMessageBubble( + text: 'Essa foto é linda', + align: DSAlign.left, + replyContent: DSReplyContent( + replied: DSReplyContentReplied( + type: 'text/plain', + value: 'Essa foto é linda', + ), + inReplyTo: DSReplyContentInReplyTo( + id: 'id', + type: 'application/vnd.lime.media-link+json', + value: + '{uri:${_sampleImages['extraLarge']!}, type: "image/jpeg"}', + direction: 'received', + ), + ), + ), + DSTextMessageBubble( + text: 'Sim', + align: DSAlign.left, + replyContent: DSReplyContent( + replied: DSReplyContentReplied( + type: 'text/plain', + value: 'Sim', + ), + inReplyTo: DSReplyContentInReplyTo( + id: 'id', + type: 'text/plain', + value: 'Você gostaria de um atendimento humano?', + direction: 'received', + ), + ), + ), + DSImageMessageBubble( + align: DSAlign.left, + url: _sampleImages['extraLarge']!, + appBarText: 'appBarText', + replyContent: DSReplyContent( + replied: DSReplyContentReplied( + type: 'text/plain', + value: + '{type: "image/jpeg", uri: ${_sampleImages['extraLarge']!}}', + ), + inReplyTo: DSReplyContentInReplyTo( + id: 'id', + type: 'text/plain', + value: 'Envie a imagem por favor', + direction: 'received', + ), + ), + ), DSApplicationJsonMessageBubble( align: DSAlign.right, borderRadius: const [ From 34b6de0f267b0687fbf1348252f864fd27a7a6cd Mon Sep 17 00:00:00 2001 From: Andre Rossi Date: Thu, 30 Nov 2023 09:29:26 -0300 Subject: [PATCH 20/22] feat: added feature toggle --- lib/src/models/ds_reply_content.model.dart | 8 ++++++-- lib/src/utils/ds_utils.util.dart | 8 ++++++++ lib/src/widgets/utils/ds_card.widget.dart | 4 +++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/src/models/ds_reply_content.model.dart b/lib/src/models/ds_reply_content.model.dart index f1e519c2..8bb86101 100644 --- a/lib/src/models/ds_reply_content.model.dart +++ b/lib/src/models/ds_reply_content.model.dart @@ -11,6 +11,10 @@ class DSReplyContent { }); DSReplyContent.fromJson(Map json) - : replied = DSReplyContentReplied.fromJson(json['replied']), - inReplyTo = DSReplyContentInReplyTo.fromJson(json['inReplyTo']); + : replied = DSReplyContentReplied.fromJson( + json['replied'], + ), + inReplyTo = DSReplyContentInReplyTo.fromJson( + json['inReplyTo'], + ); } diff --git a/lib/src/utils/ds_utils.util.dart b/lib/src/utils/ds_utils.util.dart index 626f51e8..c697b8ff 100644 --- a/lib/src/utils/ds_utils.util.dart +++ b/lib/src/utils/ds_utils.util.dart @@ -11,6 +11,9 @@ abstract class DSUtils { static const bubbleMaxSize = 480.0; static const defaultAnimationDuration = Duration(milliseconds: 300); + static bool? _shouldShowReplyContainer; + static bool? get shouldShowReplyContainer => _shouldShowReplyContainer; + static String generateUniqueID() => md5.convert(utf8.encode(DateTime.now().toIso8601String())).toString(); @@ -1084,4 +1087,9 @@ abstract class DSUtils { flag: 'zimbabwe_flag', ), ]; + + static void setShowReplyContainer({ + required final bool value, + }) => + _shouldShowReplyContainer = value; } diff --git a/lib/src/widgets/utils/ds_card.widget.dart b/lib/src/widgets/utils/ds_card.widget.dart index 6f05e57a..74a26545 100644 --- a/lib/src/widgets/utils/ds_card.widget.dart +++ b/lib/src/widgets/utils/ds_card.widget.dart @@ -13,6 +13,7 @@ import '../../models/ds_message_bubble_style.model.dart'; import '../../models/ds_reply_content.model.dart'; import '../../services/ds_file.service.dart'; import '../../utils/ds_message_content_type.util.dart'; +import '../../utils/ds_utils.util.dart'; import '../chat/audio/ds_audio_message_bubble.widget.dart'; import '../chat/ds_application_json_message_bubble.widget.dart'; import '../chat/ds_carrousel.widget.dart'; @@ -101,7 +102,8 @@ class DSCard extends StatelessWidget { onOpenLink: onOpenLink, messageId: messageId, customer: customer, - replyContent: replyContent, + replyContent: + (DSUtils.shouldShowReplyContainer ?? false) ? replyContent : null, ); case DSMessageContentType.mediaLink: From 33ec77592163331d904db4da5ecc56200091d7d7 Mon Sep 17 00:00:00 2001 From: Andre Rossi Date: Thu, 30 Nov 2023 10:32:05 -0300 Subject: [PATCH 21/22] refactor: altered variable type --- lib/src/utils/ds_utils.util.dart | 9 +-------- lib/src/widgets/utils/ds_card.widget.dart | 3 +-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/src/utils/ds_utils.util.dart b/lib/src/utils/ds_utils.util.dart index c697b8ff..b0ef6d92 100644 --- a/lib/src/utils/ds_utils.util.dart +++ b/lib/src/utils/ds_utils.util.dart @@ -10,9 +10,7 @@ abstract class DSUtils { static const bubbleMinSize = 240.0; static const bubbleMaxSize = 480.0; static const defaultAnimationDuration = Duration(milliseconds: 300); - - static bool? _shouldShowReplyContainer; - static bool? get shouldShowReplyContainer => _shouldShowReplyContainer; + static bool shouldShowReplyContainer = false; static String generateUniqueID() => md5.convert(utf8.encode(DateTime.now().toIso8601String())).toString(); @@ -1087,9 +1085,4 @@ abstract class DSUtils { flag: 'zimbabwe_flag', ), ]; - - static void setShowReplyContainer({ - required final bool value, - }) => - _shouldShowReplyContainer = value; } diff --git a/lib/src/widgets/utils/ds_card.widget.dart b/lib/src/widgets/utils/ds_card.widget.dart index 74a26545..eec23aa5 100644 --- a/lib/src/widgets/utils/ds_card.widget.dart +++ b/lib/src/widgets/utils/ds_card.widget.dart @@ -102,8 +102,7 @@ class DSCard extends StatelessWidget { onOpenLink: onOpenLink, messageId: messageId, customer: customer, - replyContent: - (DSUtils.shouldShowReplyContainer ?? false) ? replyContent : null, + replyContent: DSUtils.shouldShowReplyContainer ? replyContent : null, ); case DSMessageContentType.mediaLink: From 1d927f8b36ea9903d11021bab7205a59d0855ca5 Mon Sep 17 00:00:00 2001 From: Andre Rossi Date: Thu, 30 Nov 2023 11:33:31 -0300 Subject: [PATCH 22/22] chore: upgrade version --- CHANGELOG.md | 5 +++++ pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 314b686e..87e2b25e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.0.90 + +- [DSReplyContainer] Created the widget. +- [DSUploading] Created the widget. + ## 0.0.89 - [DSInteractiveButtonMessageBubble] Fixed border radius behavior. diff --git a/pubspec.yaml b/pubspec.yaml index 0b36c416..4f4cb247 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: blip_ds description: Blip Design System for Flutter. -version: 0.0.89 +version: 0.0.90 homepage: https://github.com/takenet/blip-ds-flutter#readme repository: https://github.com/takenet/blip-ds-flutter