diff --git a/lib/chat/views/chat_builders/vc_text_message_widget.dart b/lib/chat/views/chat_builders/vc_text_message_widget.dart index 52235f6..7ea2957 100644 --- a/lib/chat/views/chat_builders/vc_text_message_widget.dart +++ b/lib/chat/views/chat_builders/vc_text_message_widget.dart @@ -22,7 +22,6 @@ class VcTextMessageWidget extends StatelessWidget { this.timeStyle, this.showTime = true, this.showStatus = true, - this.timeAndStatusPosition = TimeAndStatusPosition.end, super.key, }); @@ -63,9 +62,6 @@ class VcTextMessageWidget extends StatelessWidget { /// for sent messages. final bool showStatus; - /// Position of the timestamp and status indicator relative to the text. - final TimeAndStatusPosition timeAndStatusPosition; - bool get _isOnlyEmoji => message.metadata?['isOnlyEmoji'] == true; @override @@ -98,62 +94,26 @@ class VcTextMessageWidget extends StatelessWidget { : textStyle, ); - return Container( - padding: _isOnlyEmoji - ? EdgeInsets.symmetric( - horizontal: (padding?.horizontal ?? 0) / 2, - // vertical: 0, - ) - : padding, - decoration: _isOnlyEmoji - ? null - : BoxDecoration( - color: backgroundColor, - borderRadius: borderRadius ?? chatTheme.shape, - ), - child: _buildContentBasedOnPosition( - context: context, - textContent: textContent, - timeAndStatus: timeAndStatus, - textStyle: textStyle, - ), - ); - } - - Widget _buildContentBasedOnPosition({ - required BuildContext context, - required Widget textContent, - TimeAndStatus? timeAndStatus, - TextStyle? textStyle, - }) { - if (timeAndStatus == null) { - return textContent; - } - - switch (timeAndStatusPosition) { - case TimeAndStatusPosition.start: - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [textContent, timeAndStatus], - ); - case TimeAndStatusPosition.inline: - return Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Flexible(child: textContent), - const SizedBox(width: 4), - timeAndStatus, - ], - ); - case TimeAndStatusPosition.end: - return Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: [textContent, timeAndStatus], - ); - } + return Column( + crossAxisAlignment: + isSentByMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, + children: [ + Container( + padding: _isOnlyEmoji + ? EdgeInsets.symmetric( + horizontal: (padding?.horizontal ?? 0) / 2, + // vertical: 0, + ) + : padding, + decoration: _isOnlyEmoji + ? null + : BoxDecoration( + color: backgroundColor, + borderRadius: borderRadius ?? chatTheme.shape, + ), + child: textContent), + if (timeAndStatus != null) timeAndStatus, + ]); } Color _resolveBackgroundColor(bool isSentByMe, ScaleChatTheme theme) { @@ -170,11 +130,8 @@ class VcTextMessageWidget extends StatelessWidget { return receivedTextStyle ?? theme.receivedMessageBodyTextStyle; } - TextStyle _resolveTimeStyle(bool isSentByMe, ScaleChatTheme theme) { - final ts = _resolveTextStyle(isSentByMe, theme); - - return theme.timeStyle.copyWith(color: ts.color); - } + TextStyle _resolveTimeStyle(bool isSentByMe, ScaleChatTheme theme) => + theme.timeStyle; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { @@ -193,9 +150,7 @@ class VcTextMessageWidget extends StatelessWidget { 'receivedTextStyle', receivedTextStyle)) ..add(DiagnosticsProperty('timeStyle', timeStyle)) ..add(DiagnosticsProperty('showTime', showTime)) - ..add(DiagnosticsProperty('showStatus', showStatus)) - ..add(EnumProperty( - 'timeAndStatusPosition', timeAndStatusPosition)); + ..add(DiagnosticsProperty('showStatus', showStatus)); } } diff --git a/lib/chat/views/chat_component_widget.dart b/lib/chat/views/chat_component_widget.dart index 8cb4edc..aecf531 100644 --- a/lib/chat/views/chat_component_widget.dart +++ b/lib/chat/views/chat_component_widget.dart @@ -261,18 +261,36 @@ class _ChatComponentWidgetState extends State { chatAnimatedListBuilder: (context, itemBuilder) => ChatAnimatedListReversed( scrollController: _scrollController, + messageGroupingTimeoutInSeconds: 60, itemBuilder: itemBuilder), // Text message builder - textMessageBuilder: (context, message, index) => - VcTextMessageWidget( - message: message, - index: index, - padding: const EdgeInsets.symmetric( - vertical: 12, horizontal: 16) - .scaled(context) - // showTime: true, - // showStatus: true, - ), + textMessageBuilder: (context, message, index) { + var showTime = true; + if (_chatController.messages.length > 1 && + index < _chatController.messages.length - 1 && + message.time != null) { + final nextMessage = + _chatController.messages[index + 1]; + if (nextMessage.time != null) { + if (nextMessage.time! + .difference(message.time!) + .inSeconds < + 60 && + nextMessage.authorId == message.authorId) { + showTime = false; + } + } + } + return VcTextMessageWidget( + message: message, + index: index, + padding: const EdgeInsets.symmetric( + vertical: 12, horizontal: 16) + .scaled(context), + showTime: showTime, + showStatus: showTime, + ); + }, // Composer builder composerBuilder: (ctx) => VcComposerWidget( autofocus: true, diff --git a/lib/chat_list/views/chat_list_widget.dart b/lib/chat_list/views/chat_list_widget.dart index 04720e8..33ddaab 100644 --- a/lib/chat_list/views/chat_list_widget.dart +++ b/lib/chat_list/views/chat_list_widget.dart @@ -56,43 +56,38 @@ class ChatListWidget extends StatelessWidget { valueMapper: (c) => c.value); final chatListV = context.watch().state; - return chatListV - .builder((context, chatList) => SizedBox.expand( - child: styledTitleContainer( - context: context, - title: translate('chat_list.chats'), - child: (chatList.isEmpty) - ? const SizedBox.expand(child: EmptyChatListWidget()) - : TapRegion( - onTapOutside: (_) { - FocusScope.of(context).unfocus(); - }, - child: SearchableList( - initialList: chatList.map((x) => x.value).toList(), - itemBuilder: (c) { - switch (c.whichKind()) { - case proto.Chat_Kind.direct: - return _itemBuilderDirect( - c.direct, - contactMap, - ); - case proto.Chat_Kind.group: - return const Text( - 'group chats not yet supported!'); - case proto.Chat_Kind.notSet: - throw StateError('unknown chat kind'); - } - }, - filter: (value) => - _itemFilter(contactMap, chatList, value), - searchFieldPadding: - const EdgeInsets.fromLTRB(0, 0, 0, 4), - inputDecoration: InputDecoration( - labelText: translate('chat_list.search'), - ), - )).paddingAll(8), - ))) - .paddingLTRB(8, 0, 8, 8); + return chatListV.builder((context, chatList) => SizedBox.expand( + child: styledContainer( + context: context, + child: (chatList.isEmpty) + ? const SizedBox.expand(child: EmptyChatListWidget()) + : TapRegion( + onTapOutside: (_) { + FocusScope.of(context).unfocus(); + }, + child: SearchableList( + initialList: chatList.map((x) => x.value).toList(), + itemBuilder: (c) { + switch (c.whichKind()) { + case proto.Chat_Kind.direct: + return _itemBuilderDirect( + c.direct, + contactMap, + ); + case proto.Chat_Kind.group: + return const Text('group chats not yet supported!'); + case proto.Chat_Kind.notSet: + throw StateError('unknown chat kind'); + } + }, + filter: (value) => + _itemFilter(contactMap, chatList, value), + searchFieldPadding: const EdgeInsets.fromLTRB(0, 0, 0, 4), + inputDecoration: InputDecoration( + labelText: translate('chat_list.search'), + ), + )).paddingAll(8), + ))); }); } } diff --git a/lib/theme/models/scale_theme/scale_chat_theme.dart b/lib/theme/models/scale_theme/scale_chat_theme.dart index 5da0fe2..d1145cf 100644 --- a/lib/theme/models/scale_theme/scale_chat_theme.dart +++ b/lib/theme/models/scale_theme/scale_chat_theme.dart @@ -354,7 +354,10 @@ extension ScaleChatThemeExt on ScaleTheme { : scheme.primaryScale.calloutText, ), onlyEmojiFontSize: 64, - timeStyle: textTheme.bodySmall!.copyWith(fontSize: 9), + timeStyle: textTheme.bodySmall!.copyWith(fontSize: 9).copyWith( + color: config.preferBorders || config.useVisualIndicators + ? scheme.primaryScale.calloutBackground + : scheme.primaryScale.borderText), receivedMessageBodyTextStyle: textTheme.bodyLarge!.copyWith( color: config.preferBorders ? scheme.secondaryScale.calloutBackground diff --git a/lib/theme/views/widget_helpers.dart b/lib/theme/views/widget_helpers.dart index a02f207..2d8d626 100644 --- a/lib/theme/views/widget_helpers.dart +++ b/lib/theme/views/widget_helpers.dart @@ -464,6 +464,38 @@ Widget styledTitleContainer({ ])); } +Widget styledContainer({ + required BuildContext context, + required Widget child, + Color? borderColor, + Color? backgroundColor, +}) { + final theme = Theme.of(context); + final scale = theme.extension()!; + final scaleConfig = theme.extension()!; + + return DecoratedBox( + decoration: ShapeDecoration( + color: borderColor ?? scale.primaryScale.border, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale), + )), + child: Column(children: [ + DecoratedBox( + decoration: ShapeDecoration( + color: + backgroundColor ?? scale.primaryScale.subtleBackground, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 8 * scaleConfig.borderRadiusScale), + )), + child: child) + .paddingAll(4) + .expanded() + ])); +} + Widget styledCard({ required BuildContext context, required Widget child,