diff --git a/lib/widgets/content.dart b/lib/widgets/content.dart index ed7613e3f6..55b5331c3c 100644 --- a/lib/widgets/content.dart +++ b/lib/widgets/content.dart @@ -883,7 +883,7 @@ class WebsitePreview extends StatelessWidget { // TODO(#488) use different color for non-message contexts // TODO(#647) use different color for highlighted messages // TODO(#681) use different color for DM messages - color: MessageListTheme.of(context).streamMessageBgDefault, + color: MessageListTheme.of(context).bgMessageRegular, child: ClipRect( child: ConstrainedBox( constraints: BoxConstraints(maxHeight: 80), diff --git a/lib/widgets/message_list.dart b/lib/widgets/message_list.dart index c4ca22bce3..2cc9e2963e 100644 --- a/lib/widgets/message_list.dart +++ b/lib/widgets/message_list.dart @@ -14,6 +14,7 @@ import '../model/typing_status.dart'; import 'action_sheet.dart'; import 'actions.dart'; import 'app_bar.dart'; +import 'color.dart'; import 'compose_box.dart'; import 'content.dart'; import 'emoji_reaction.dart'; @@ -28,14 +29,10 @@ import 'theme.dart'; /// Message-list styles that differ between light and dark themes. class MessageListTheme extends ThemeExtension { static final light = MessageListTheme._( - dateSeparator: Colors.black, - dateSeparatorText: const HSLColor.fromAHSL(0.75, 0, 0, 0.15).toColor(), + bgMessageRegular: const HSLColor.fromAHSL(1, 0, 0, 1).toColor(), dmRecipientHeaderBg: const HSLColor.fromAHSL(1, 46, 0.35, 0.93).toColor(), - messageTimestamp: const HSLColor.fromAHSL(0.8, 0, 0, 0.2).toColor(), - recipientHeaderText: const HSLColor.fromAHSL(1, 0, 0, 0.15).toColor(), + labelTime: const HSLColor.fromAHSL(0.49, 0, 0, 0).toColor(), senderBotIcon: const HSLColor.fromAHSL(1, 180, 0.08, 0.65).toColor(), - senderName: const HSLColor.fromAHSL(1, 0, 0, 0.2).toColor(), - streamMessageBgDefault: Colors.white, streamRecipientHeaderChevronRight: Colors.black.withValues(alpha: 0.3), // From the Figma mockup at: @@ -53,14 +50,10 @@ class MessageListTheme extends ThemeExtension { ); static final dark = MessageListTheme._( - dateSeparator: Colors.white, - dateSeparatorText: const HSLColor.fromAHSL(0.75, 0, 0, 1).toColor(), + bgMessageRegular: const HSLColor.fromAHSL(1, 0, 0, 0.11).toColor(), dmRecipientHeaderBg: const HSLColor.fromAHSL(1, 46, 0.15, 0.2).toColor(), - messageTimestamp: const HSLColor.fromAHSL(0.8, 0, 0, 0.85).toColor(), - recipientHeaderText: const HSLColor.fromAHSL(0.8, 0, 0, 1).toColor(), + labelTime: const HSLColor.fromAHSL(0.5, 0, 0, 1).toColor(), senderBotIcon: const HSLColor.fromAHSL(1, 180, 0.05, 0.5).toColor(), - senderName: const HSLColor.fromAHSL(0.85, 0, 0, 1).toColor(), - streamMessageBgDefault: const HSLColor.fromAHSL(1, 0, 0, 0.15).toColor(), streamRecipientHeaderChevronRight: Colors.white.withValues(alpha: 0.3), // 0.75 opacity from here: @@ -77,14 +70,10 @@ class MessageListTheme extends ThemeExtension { ); MessageListTheme._({ - required this.dateSeparator, - required this.dateSeparatorText, + required this.bgMessageRegular, required this.dmRecipientHeaderBg, - required this.messageTimestamp, - required this.recipientHeaderText, + required this.labelTime, required this.senderBotIcon, - required this.senderName, - required this.streamMessageBgDefault, required this.streamRecipientHeaderChevronRight, required this.unreadMarker, required this.unreadMarkerGap, @@ -101,14 +90,10 @@ class MessageListTheme extends ThemeExtension { return extension!; } - final Color dateSeparator; - final Color dateSeparatorText; + final Color bgMessageRegular; final Color dmRecipientHeaderBg; - final Color messageTimestamp; - final Color recipientHeaderText; + final Color labelTime; final Color senderBotIcon; - final Color senderName; - final Color streamMessageBgDefault; final Color streamRecipientHeaderChevronRight; final Color unreadMarker; final Color unreadMarkerGap; @@ -116,28 +101,20 @@ class MessageListTheme extends ThemeExtension { @override MessageListTheme copyWith({ - Color? dateSeparator, - Color? dateSeparatorText, + Color? bgMessageRegular, Color? dmRecipientHeaderBg, - Color? messageTimestamp, - Color? recipientHeaderText, + Color? labelTime, Color? senderBotIcon, - Color? senderName, - Color? streamMessageBgDefault, Color? streamRecipientHeaderChevronRight, Color? unreadMarker, Color? unreadMarkerGap, Color? unsubscribedStreamRecipientHeaderBg, }) { return MessageListTheme._( - dateSeparator: dateSeparator ?? this.dateSeparator, - dateSeparatorText: dateSeparatorText ?? this.dateSeparatorText, + bgMessageRegular: bgMessageRegular ?? this.bgMessageRegular, dmRecipientHeaderBg: dmRecipientHeaderBg ?? this.dmRecipientHeaderBg, - messageTimestamp: messageTimestamp ?? this.messageTimestamp, - recipientHeaderText: recipientHeaderText ?? this.recipientHeaderText, + labelTime: labelTime ?? this.labelTime, senderBotIcon: senderBotIcon ?? this.senderBotIcon, - senderName: senderName ?? this.senderName, - streamMessageBgDefault: streamMessageBgDefault ?? this.streamMessageBgDefault, streamRecipientHeaderChevronRight: streamRecipientHeaderChevronRight ?? this.streamRecipientHeaderChevronRight, unreadMarker: unreadMarker ?? this.unreadMarker, unreadMarkerGap: unreadMarkerGap ?? this.unreadMarkerGap, @@ -151,14 +128,10 @@ class MessageListTheme extends ThemeExtension { return this; } return MessageListTheme._( - dateSeparator: Color.lerp(dateSeparator, other.dateSeparator, t)!, - dateSeparatorText: Color.lerp(dateSeparatorText, other.dateSeparatorText, t)!, + bgMessageRegular: Color.lerp(bgMessageRegular, other.bgMessageRegular, t)!, dmRecipientHeaderBg: Color.lerp(dmRecipientHeaderBg, other.dmRecipientHeaderBg, t)!, - messageTimestamp: Color.lerp(messageTimestamp, other.messageTimestamp, t)!, - recipientHeaderText: Color.lerp(recipientHeaderText, other.recipientHeaderText, t)!, + labelTime: Color.lerp(labelTime, other.labelTime, t)!, senderBotIcon: Color.lerp(senderBotIcon, other.senderBotIcon, t)!, - senderName: Color.lerp(senderName, other.senderName, t)!, - streamMessageBgDefault: Color.lerp(streamMessageBgDefault, other.streamMessageBgDefault, t)!, streamRecipientHeaderChevronRight: Color.lerp(streamRecipientHeaderChevronRight, other.streamRecipientHeaderChevronRight, t)!, unreadMarker: Color.lerp(unreadMarker, other.unreadMarker, t)!, unreadMarkerGap: Color.lerp(unreadMarkerGap, other.unreadMarkerGap, t)!, @@ -373,8 +346,7 @@ class MessageListAppBarTitle extends StatelessWidget { Padding( padding: const EdgeInsetsDirectional.only(start: 4), child: Icon(icon, - // TODO(design) copies the recipient header in web; is there a better color? - color: designVariables.colorMessageHeaderIconInteractive, size: 14)), + color: designVariables.title.withFadedAlpha(0.5), size: 14)), ]); } @@ -930,11 +902,12 @@ class DateSeparator extends StatelessWidget { const textBottomPadding = 2.0; final messageListTheme = MessageListTheme.of(context); + final designVariables = DesignVariables.of(context); - final line = BorderSide(width: 0, color: messageListTheme.dateSeparator); + final line = BorderSide(width: 0, color: designVariables.foreground); // TODO(#681) use different color for DM messages - return ColoredBox(color: messageListTheme.streamMessageBgDefault, + return ColoredBox(color: messageListTheme.bgMessageRegular, child: Padding( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 2), child: Row(children: [ @@ -981,7 +954,7 @@ class MessageItem extends StatelessWidget { child: _UnreadMarker( isRead: message.flags.contains(MessageFlag.read), child: ColoredBox( - color: messageListTheme.streamMessageBgDefault, + color: messageListTheme.bgMessageRegular, child: Column(children: [ MessageWithPossibleSender(item: item), if (trailingWhitespace != null && item.isLastInBlock) SizedBox(height: trailingWhitespace!), @@ -1069,7 +1042,7 @@ class StreamMessageRecipientHeader extends StatelessWidget { iconColor = swatch.iconOnBarBackground; } else { backgroundColor = messageListTheme.unsubscribedStreamRecipientHeaderBg; - iconColor = messageListTheme.recipientHeaderText; + iconColor = designVariables.title; } final Widget streamWidget; @@ -1125,8 +1098,7 @@ class StreamMessageRecipientHeader extends StatelessWidget { overflow: TextOverflow.ellipsis, style: recipientHeaderTextStyle(context))), const SizedBox(width: 4), - // TODO(design) copies the recipient header in web; is there a better color? - Icon(size: 14, color: designVariables.colorMessageHeaderIconInteractive, + Icon(size: 14, color: designVariables.title.withFadedAlpha(0.5), // A null [Icon.icon] makes a blank space. iconDataForTopicVisibilityPolicy( store.topicVisibilityPolicy(message.streamId, topic))), @@ -1186,6 +1158,7 @@ class DmRecipientHeader extends StatelessWidget { } final messageListTheme = MessageListTheme.of(context); + final designVariables = DesignVariables.of(context); return GestureDetector( // When already in a DM narrow, disable tap interaction that would just @@ -1206,7 +1179,7 @@ class DmRecipientHeader extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(horizontal: 6), child: Icon( - color: messageListTheme.recipientHeaderText, + color: designVariables.title, size: 16, ZulipIcons.user)), Expanded( @@ -1220,7 +1193,7 @@ class DmRecipientHeader extends StatelessWidget { TextStyle recipientHeaderTextStyle(BuildContext context) { return TextStyle( - color: MessageListTheme.of(context).recipientHeaderText, + color: DesignVariables.of(context).title, fontSize: 16, letterSpacing: proportionalLetterSpacing(context, 0.02, baseFontSize: 16), height: (18 / 16), @@ -1264,7 +1237,7 @@ class DateText extends StatelessWidget { final zulipLocalizations = ZulipLocalizations.of(context); return Text( style: TextStyle( - color: messageListTheme.dateSeparatorText, + color: messageListTheme.labelTime, fontSize: fontSize, height: height, // This is equivalent to css `all-small-caps`, see: @@ -1356,7 +1329,7 @@ class MessageWithPossibleSender extends StatelessWidget { style: TextStyle( fontSize: 18, height: (22 / 18), - color: messageListTheme.senderName, + color: designVariables.title, ).merge(weightVariableTextStyle(context, wght: 600)), overflow: TextOverflow.ellipsis)), if (sender?.isBot ?? false) ...[ @@ -1371,7 +1344,7 @@ class MessageWithPossibleSender extends StatelessWidget { const SizedBox(width: 4), Text(time, style: TextStyle( - color: messageListTheme.messageTimestamp, + color: messageListTheme.labelTime, fontSize: 16, height: (18 / 16), fontFeatures: const [FontFeature.enable('c2sc'), FontFeature.enable('smcp')], diff --git a/lib/widgets/theme.dart b/lib/widgets/theme.dart index ec8ad8aecc..ed3380e005 100644 --- a/lib/widgets/theme.dart +++ b/lib/widgets/theme.dart @@ -150,7 +150,6 @@ class DesignVariables extends ThemeExtension { bgSearchInput: const Color(0xffe3e3e3), textMessage: const Color(0xff262626), channelColorSwatches: ChannelColorSwatches.light, - colorMessageHeaderIconInteractive: Colors.black.withValues(alpha: 0.2), contextMenuCancelBg: const Color(0xff797986).withValues(alpha: 0.15), contextMenuCancelPressedBg: const Color(0xff797986).withValues(alpha: 0.20), dmHeaderBg: const HSLColor.fromAHSL(1, 46, 0.35, 0.93).toColor(), @@ -195,14 +194,13 @@ class DesignVariables extends ThemeExtension { labelMenuButton: const Color(0xffffffff).withValues(alpha: 0.85), mainBackground: const Color(0xff1d1d1d), textInput: const Color(0xffffffff).withValues(alpha: 0.9), - title: const Color(0xffffffff), + title: const Color(0xffffffff).withValues(alpha: 0.9), bgSearchInput: const Color(0xff313131), textMessage: const Color(0xffffffff).withValues(alpha: 0.8), channelColorSwatches: ChannelColorSwatches.dark, contextMenuCancelBg: const Color(0xff797986).withValues(alpha: 0.15), // the same as the light mode in Figma contextMenuCancelPressedBg: const Color(0xff797986).withValues(alpha: 0.20), // the same as the light mode in Figma // TODO(design-dark) need proper dark-theme color (this is ad hoc) - colorMessageHeaderIconInteractive: Colors.white.withValues(alpha: 0.2), dmHeaderBg: const HSLColor.fromAHSL(1, 46, 0.15, 0.2).toColor(), // TODO(design-dark) need proper dark-theme color (this is ad hoc) groupDmConversationIcon: Colors.white.withValues(alpha: 0.5), @@ -256,7 +254,6 @@ class DesignVariables extends ThemeExtension { required this.bgSearchInput, required this.textMessage, required this.channelColorSwatches, - required this.colorMessageHeaderIconInteractive, required this.contextMenuCancelBg, required this.contextMenuCancelPressedBg, required this.dmHeaderBg, @@ -318,7 +315,6 @@ class DesignVariables extends ThemeExtension { final ChannelColorSwatches channelColorSwatches; // Not named variables in Figma; taken from older Figma drafts, or elsewhere. - final Color colorMessageHeaderIconInteractive; final Color contextMenuCancelBg; // In Figma, but unnamed. final Color contextMenuCancelPressedBg; // In Figma, but unnamed. final Color dmHeaderBg; @@ -367,7 +363,6 @@ class DesignVariables extends ThemeExtension { Color? bgSearchInput, Color? textMessage, ChannelColorSwatches? channelColorSwatches, - Color? colorMessageHeaderIconInteractive, Color? contextMenuCancelBg, Color? contextMenuCancelPressedBg, Color? dmHeaderBg, @@ -415,7 +410,6 @@ class DesignVariables extends ThemeExtension { bgSearchInput: bgSearchInput ?? this.bgSearchInput, textMessage: textMessage ?? this.textMessage, channelColorSwatches: channelColorSwatches ?? this.channelColorSwatches, - colorMessageHeaderIconInteractive: colorMessageHeaderIconInteractive ?? this.colorMessageHeaderIconInteractive, contextMenuCancelBg: contextMenuCancelBg ?? this.contextMenuCancelBg, contextMenuCancelPressedBg: contextMenuCancelPressedBg ?? this.contextMenuCancelPressedBg, dmHeaderBg: dmHeaderBg ?? this.dmHeaderBg, @@ -470,7 +464,6 @@ class DesignVariables extends ThemeExtension { bgSearchInput: Color.lerp(bgSearchInput, other.bgSearchInput, t)!, textMessage: Color.lerp(textMessage, other.textMessage, t)!, channelColorSwatches: ChannelColorSwatches.lerp(channelColorSwatches, other.channelColorSwatches, t), - colorMessageHeaderIconInteractive: Color.lerp(colorMessageHeaderIconInteractive, other.colorMessageHeaderIconInteractive, t)!, contextMenuCancelBg: Color.lerp(contextMenuCancelBg, other.contextMenuCancelBg, t)!, contextMenuCancelPressedBg: Color.lerp(contextMenuCancelPressedBg, other.contextMenuCancelPressedBg, t)!, dmHeaderBg: Color.lerp(dmHeaderBg, other.dmHeaderBg, t)!, diff --git a/test/widgets/message_list_test.dart b/test/widgets/message_list_test.dart index b5d3844c1a..7107e936ca 100644 --- a/test/widgets/message_list_test.dart +++ b/test/widgets/message_list_test.dart @@ -268,17 +268,17 @@ void main() { return widget.color; } - check(backgroundColor()).isSameColorAs(MessageListTheme.light.streamMessageBgDefault); + check(backgroundColor()).isSameColorAs(MessageListTheme.light.bgMessageRegular); tester.platformDispatcher.platformBrightnessTestValue = Brightness.dark; await tester.pump(); await tester.pump(kThemeAnimationDuration * 0.4); final expectedLerped = MessageListTheme.light.lerp(MessageListTheme.dark, 0.4); - check(backgroundColor()).isSameColorAs(expectedLerped.streamMessageBgDefault); + check(backgroundColor()).isSameColorAs(expectedLerped.bgMessageRegular); await tester.pump(kThemeAnimationDuration * 0.6); - check(backgroundColor()).isSameColorAs(MessageListTheme.dark.streamMessageBgDefault); + check(backgroundColor()).isSameColorAs(MessageListTheme.dark.bgMessageRegular); }); group('fetch initial batch of messages', () {