Skip to content

Commit 9a0f7da

Browse files
committed
msglist: Implement new edited/moved label design.
This replaces the gutter edit state marker design. Since it leads to message displacement, a previous test case is no longer applicable. We also inline part of what was previously in edit_state_marker.dart, because the new design simplifies the implementation. The design variables are taken from the Figma design: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=3038-56393&t=WMT80mwuFaruNbVf-1 Signed-off-by: Zixuan James Li <[email protected]>
1 parent f7bc237 commit 9a0f7da

File tree

5 files changed

+48
-156
lines changed

5 files changed

+48
-156
lines changed

assets/l10n/app_en.arb

+8
Original file line numberDiff line numberDiff line change
@@ -533,5 +533,13 @@
533533
"manyPeopleTyping": "Several people are typing…",
534534
"@manyPeopleTyping": {
535535
"description": "Text to display when there are multiple users typing."
536+
},
537+
"messageIsEditedLabel": "EDITED",
538+
"@messageIsEditedLabel": {
539+
"description": "Label for an edited message. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)"
540+
},
541+
"messageIsMovedLabel": "MOVED",
542+
"@messageIsMovedLabel": {
543+
"description": "Label for a moved message. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)"
536544
}
537545
}

lib/widgets/edit_state_marker.dart

-74
This file was deleted.

lib/widgets/message_list.dart

+30-12
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import 'page.dart';
2222
import 'profile.dart';
2323
import 'sticky_header.dart';
2424
import 'store.dart';
25-
import 'edit_state_marker.dart';
2625
import 'text.dart';
2726
import 'theme.dart';
2827

@@ -33,7 +32,7 @@ class MessageListTheme extends ThemeExtension<MessageListTheme> {
3332
dateSeparator: Colors.black,
3433
dateSeparatorText: const HSLColor.fromAHSL(0.75, 0, 0, 0.15).toColor(),
3534
dmRecipientHeaderBg: const HSLColor.fromAHSL(1, 46, 0.35, 0.93).toColor(),
36-
editedMovedMarkerCollapsed: const Color.fromARGB(128, 146, 167, 182),
35+
editStateLabel: const HSLColor.fromAHSL(0.35, 0, 0, 0).toColor(),
3736
messageTimestamp: const HSLColor.fromAHSL(0.8, 0, 0, 0.2).toColor(),
3837
recipientHeaderText: const HSLColor.fromAHSL(1, 0, 0, 0.15).toColor(),
3938
senderBotIcon: const HSLColor.fromAHSL(1, 180, 0.08, 0.65).toColor(),
@@ -60,8 +59,7 @@ class MessageListTheme extends ThemeExtension<MessageListTheme> {
6059
dateSeparator: Colors.white,
6160
dateSeparatorText: const HSLColor.fromAHSL(0.75, 0, 0, 1).toColor(),
6261
dmRecipientHeaderBg: const HSLColor.fromAHSL(1, 46, 0.15, 0.2).toColor(),
63-
// TODO(design-dark) need proper dark-theme color (this is ad hoc)
64-
editedMovedMarkerCollapsed: const Color.fromARGB(128, 214, 202, 194),
62+
editStateLabel: const HSLColor.fromAHSL(0.35, 0, 0, 1).toColor(),
6563
messageTimestamp: const HSLColor.fromAHSL(0.6, 0, 0, 1).toColor(),
6664
recipientHeaderText: const HSLColor.fromAHSL(0.8, 0, 0, 1).toColor(),
6765
senderBotIcon: const HSLColor.fromAHSL(1, 180, 0.05, 0.5).toColor(),
@@ -86,7 +84,7 @@ class MessageListTheme extends ThemeExtension<MessageListTheme> {
8684
required this.dateSeparator,
8785
required this.dateSeparatorText,
8886
required this.dmRecipientHeaderBg,
89-
required this.editedMovedMarkerCollapsed,
87+
required this.editStateLabel,
9088
required this.messageTimestamp,
9189
required this.recipientHeaderText,
9290
required this.senderBotIcon,
@@ -111,7 +109,7 @@ class MessageListTheme extends ThemeExtension<MessageListTheme> {
111109
final Color dateSeparator;
112110
final Color dateSeparatorText;
113111
final Color dmRecipientHeaderBg;
114-
final Color editedMovedMarkerCollapsed;
112+
final Color editStateLabel;
115113
final Color messageTimestamp;
116114
final Color recipientHeaderText;
117115
final Color senderBotIcon;
@@ -127,7 +125,7 @@ class MessageListTheme extends ThemeExtension<MessageListTheme> {
127125
Color? dateSeparator,
128126
Color? dateSeparatorText,
129127
Color? dmRecipientHeaderBg,
130-
Color? editedMovedMarkerCollapsed,
128+
Color? editStateLabel,
131129
Color? messageTimestamp,
132130
Color? recipientHeaderText,
133131
Color? senderBotIcon,
@@ -142,7 +140,7 @@ class MessageListTheme extends ThemeExtension<MessageListTheme> {
142140
dateSeparator: dateSeparator ?? this.dateSeparator,
143141
dateSeparatorText: dateSeparatorText ?? this.dateSeparatorText,
144142
dmRecipientHeaderBg: dmRecipientHeaderBg ?? this.dmRecipientHeaderBg,
145-
editedMovedMarkerCollapsed: editedMovedMarkerCollapsed ?? this.editedMovedMarkerCollapsed,
143+
editStateLabel: editStateLabel ?? this.editStateLabel,
146144
messageTimestamp: messageTimestamp ?? this.messageTimestamp,
147145
recipientHeaderText: recipientHeaderText ?? this.recipientHeaderText,
148146
senderBotIcon: senderBotIcon ?? this.senderBotIcon,
@@ -164,7 +162,7 @@ class MessageListTheme extends ThemeExtension<MessageListTheme> {
164162
dateSeparator: Color.lerp(dateSeparator, other.dateSeparator, t)!,
165163
dateSeparatorText: Color.lerp(dateSeparatorText, other.dateSeparatorText, t)!,
166164
dmRecipientHeaderBg: Color.lerp(streamMessageBgDefault, other.dmRecipientHeaderBg, t)!,
167-
editedMovedMarkerCollapsed: Color.lerp(editedMovedMarkerCollapsed, other.editedMovedMarkerCollapsed, t)!,
165+
editStateLabel: Color.lerp(editStateLabel, other.editStateLabel, t)!,
168166
messageTimestamp: Color.lerp(messageTimestamp, other.messageTimestamp, t)!,
169167
recipientHeaderText: Color.lerp(recipientHeaderText, other.recipientHeaderText, t)!,
170168
senderBotIcon: Color.lerp(senderBotIcon, other.senderBotIcon, t)!,
@@ -1256,6 +1254,16 @@ class MessageWithPossibleSender extends StatelessWidget {
12561254
]);
12571255
}
12581256

1257+
final localizations = ZulipLocalizations.of(context);
1258+
String? editStateText;
1259+
switch (message.editState) {
1260+
case MessageEditState.edited:
1261+
editStateText = localizations.messageIsEditedLabel;
1262+
case MessageEditState.moved:
1263+
editStateText = localizations.messageIsMovedLabel;
1264+
case MessageEditState.none:
1265+
}
1266+
12591267
return GestureDetector(
12601268
behavior: HitTestBehavior.translucent,
12611269
onLongPress: () => showMessageActionSheet(context: context, message: message),
@@ -1265,15 +1273,25 @@ class MessageWithPossibleSender extends StatelessWidget {
12651273
if (senderRow != null)
12661274
Padding(padding: const EdgeInsets.fromLTRB(16, 2, 16, 0),
12671275
child: senderRow),
1268-
EditStateMarker(
1269-
editState: message.editState,
1276+
Row(
1277+
crossAxisAlignment: CrossAxisAlignment.baseline,
1278+
textBaseline: localizedTextBaseline(context),
12701279
children: [
1280+
const SizedBox(width: 16),
12711281
Expanded(child: Column(
12721282
crossAxisAlignment: CrossAxisAlignment.stretch,
12731283
children: [
12741284
MessageContent(message: message, content: item.content),
1285+
if (editStateText != null)
1286+
Text(editStateText, textAlign: TextAlign.end,
1287+
style: TextStyle(
1288+
color: designVariables.labelEdited,
1289+
fontSize: 12,
1290+
height: (12 / 12),
1291+
letterSpacing: proportionalLetterSpacing(
1292+
context, 0.05, baseFontSize: 12))),
12751293
if ((message.reactions?.total ?? 0) > 0)
1276-
ReactionChipsList(messageId: message.id, reactions: message.reactions!)
1294+
ReactionChipsList(messageId: message.id, reactions: message.reactions!),
12771295
])),
12781296
SizedBox(width: 16,
12791297
child: message.flags.contains(MessageFlag.starred)

lib/widgets/theme.dart

+7
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
112112
borderBar: const Color(0x33000000),
113113
icon: const Color(0xff666699),
114114
labelCounterUnread: const Color(0xff222222),
115+
labelEdited: const HSLColor.fromAHSL(0.35, 0, 0, 0).toColor(),
115116
labelMenuButton: const Color(0xff222222),
116117
mainBackground: const Color(0xfff0f0f0),
117118
title: const Color(0xff1a1a1a),
@@ -140,6 +141,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
140141
borderBar: Colors.black.withOpacity(0.41),
141142
icon: const Color(0xff7070c2),
142143
labelCounterUnread: const Color(0xffffffff).withOpacity(0.7),
144+
labelEdited: const HSLColor.fromAHSL(0.35, 0, 0, 1).toColor(),
143145
labelMenuButton: const Color(0xffffffff).withOpacity(0.85),
144146
mainBackground: const Color(0xff1d1d1d),
145147
title: const Color(0xffffffff),
@@ -174,6 +176,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
174176
required this.borderBar,
175177
required this.icon,
176178
required this.labelCounterUnread,
179+
required this.labelEdited,
177180
required this.labelMenuButton,
178181
required this.mainBackground,
179182
required this.title,
@@ -210,6 +213,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
210213
final Color borderBar;
211214
final Color icon;
212215
final Color labelCounterUnread;
216+
final Color labelEdited;
213217
final Color labelMenuButton;
214218
final Color mainBackground;
215219
final Color title;
@@ -241,6 +245,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
241245
Color? borderBar,
242246
Color? icon,
243247
Color? labelCounterUnread,
248+
Color? labelEdited,
244249
Color? labelMenuButton,
245250
Color? mainBackground,
246251
Color? title,
@@ -267,6 +272,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
267272
borderBar: borderBar ?? this.borderBar,
268273
icon: icon ?? this.icon,
269274
labelCounterUnread: labelCounterUnread ?? this.labelCounterUnread,
275+
labelEdited: labelEdited ?? this.labelEdited,
270276
labelMenuButton: labelMenuButton ?? this.labelMenuButton,
271277
mainBackground: mainBackground ?? this.mainBackground,
272278
title: title ?? this.title,
@@ -300,6 +306,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
300306
borderBar: Color.lerp(borderBar, other.borderBar, t)!,
301307
icon: Color.lerp(icon, other.icon, t)!,
302308
labelCounterUnread: Color.lerp(labelCounterUnread, other.labelCounterUnread, t)!,
309+
labelEdited: Color.lerp(labelEdited, other.labelEdited, t)!,
303310
labelMenuButton: Color.lerp(labelMenuButton, other.labelMenuButton, t)!,
304311
mainBackground: Color.lerp(mainBackground, other.mainBackground, t)!,
305312
title: Color.lerp(title, other.title, t)!,

test/widgets/message_list_test.dart

+3-70
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import 'package:zulip/model/narrow.dart';
1616
import 'package:zulip/model/store.dart';
1717
import 'package:zulip/widgets/autocomplete.dart';
1818
import 'package:zulip/widgets/content.dart';
19-
import 'package:zulip/widgets/emoji_reaction.dart';
2019
import 'package:zulip/widgets/icons.dart';
2120
import 'package:zulip/widgets/message_list.dart';
2221
import 'package:zulip/widgets/store.dart';
@@ -1038,10 +1037,10 @@ void main() {
10381037
});
10391038
});
10401039

1041-
group('EditStateMarker', () {
1040+
group('edit state label', () {
10421041
void checkMarkersCount({required int edited, required int moved}) {
1043-
check(find.byIcon(ZulipIcons.edited).evaluate()).length.equals(edited);
1044-
check(find.byIcon(ZulipIcons.message_moved).evaluate()).length.equals(moved);
1042+
check(find.text('EDITED').evaluate()).length.equals(edited);
1043+
check(find.text('MOVED').evaluate()).length.equals(moved);
10451044
}
10461045

10471046
testWidgets('no edited or moved messages', (tester) async {
@@ -1069,72 +1068,6 @@ void main() {
10691068
await tester.pump();
10701069
checkMarkersCount(edited: 2, moved: 0);
10711070
});
1072-
1073-
List<List<(String, Rect)>> captureMessageRects(
1074-
WidgetTester tester,
1075-
List<Message> messages,
1076-
Message targetMessage,
1077-
) {
1078-
assert(messages.contains(targetMessage));
1079-
final List<List<(String, Rect)>> result = [];
1080-
for (final message in messages) {
1081-
final baseFinder = find.byWidgetPredicate(
1082-
(widget) => widget is MessageWithPossibleSender
1083-
&& widget.item.message.id == message.id);
1084-
1085-
Rect getRectMatching(Finder matching) {
1086-
final finder = find.descendant(
1087-
of: baseFinder, matching: matching)..tryEvaluate();
1088-
check(finder.found, because: '${message.content}: $matching')
1089-
.length.equals(1);
1090-
return tester.getRect(finder.first);
1091-
}
1092-
1093-
result.add([
1094-
('MessageWithPossibleSender', tester.getRect(baseFinder)),
1095-
('MessageContent', getRectMatching(find.byType(MessageContent))),
1096-
('Paragraph', getRectMatching(find.byType(Paragraph))),
1097-
if (message.id == targetMessage.id) ...[
1098-
('Star', getRectMatching(find.byIcon(ZulipIcons.star_filled))),
1099-
('ReactionChipsList', getRectMatching(find.byType(ReactionChipsList))),
1100-
],
1101-
]);
1102-
}
1103-
return result;
1104-
}
1105-
1106-
testWidgets('edit state updates do not affect layout', (tester) async {
1107-
final messages = [
1108-
eg.streamMessage(topic: 'orig'),
1109-
eg.streamMessage(
1110-
topic: 'orig',
1111-
reactions: [eg.unicodeEmojiReaction, eg.realmEmojiReaction],
1112-
flags: [MessageFlag.starred]),
1113-
eg.streamMessage(topic: 'orig'),
1114-
];
1115-
final StreamMessage messageWithMarker = messages[1];
1116-
await setupMessageListPage(tester, messages: messages);
1117-
final rectsBefore = captureMessageRects(tester, messages, messageWithMarker);
1118-
checkMarkersCount(edited: 0, moved: 0);
1119-
1120-
await store.handleEvent(eg.updateMessageEventMoveFrom(
1121-
origMessages: [store.messages[messageWithMarker.id] as StreamMessage],
1122-
newTopic: 'new'));
1123-
await tester.pump();
1124-
await store.handleEvent(eg.updateMessageEventMoveFrom(
1125-
origMessages: [store.messages[messageWithMarker.id] as StreamMessage],
1126-
newTopic: 'orig'));
1127-
await tester.pump();
1128-
check(captureMessageRects(tester, messages, messageWithMarker))
1129-
.deepEquals(rectsBefore);
1130-
checkMarkersCount(edited: 0, moved: 1);
1131-
1132-
await store.handleEvent(eg.updateMessageEditEvent(messageWithMarker));
1133-
await tester.pump();
1134-
check(captureMessageRects(tester, messages, messageWithMarker))
1135-
.deepEquals(rectsBefore);
1136-
checkMarkersCount(edited: 1, moved: 0);
1137-
});
11381071
});
11391072

11401073
group('_UnreadMarker animations', () {

0 commit comments

Comments
 (0)