Skip to content

Commit 0fcc79a

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 colors 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 67f8429 commit 0fcc79a

File tree

4 files changed

+40
-156
lines changed

4 files changed

+40
-156
lines changed

assets/l10n/app_en.arb

+8
Original file line numberDiff line numberDiff line change
@@ -529,5 +529,13 @@
529529
"manyPeopleTyping": "Several people are typing…",
530530
"@manyPeopleTyping": {
531531
"description": "Text to display when there are multiple users typing."
532+
},
533+
"messageIsEditedLabel": "EDITED",
534+
"@messageIsEditedLabel": {
535+
"description": "Label for an edited message."
536+
},
537+
"messageIsMovedLabel": "MOVED",
538+
"@messageIsMovedLabel": {
539+
"description": "Label for a moved message."
532540
}
533541
}

lib/widgets/edit_state_marker.dart

-74
This file was deleted.

lib/widgets/message_list.dart

+29-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)!,
@@ -1253,6 +1251,16 @@ class MessageWithPossibleSender extends StatelessWidget {
12531251
]);
12541252
}
12551253

1254+
final localizations = ZulipLocalizations.of(context);
1255+
String? editStateText;
1256+
switch (message.editState) {
1257+
case MessageEditState.edited:
1258+
editStateText = localizations.messageIsEditedLabel;
1259+
case MessageEditState.moved:
1260+
editStateText = localizations.messageIsMovedLabel;
1261+
case MessageEditState.none:
1262+
}
1263+
12561264
return GestureDetector(
12571265
behavior: HitTestBehavior.translucent,
12581266
onLongPress: () => showMessageActionSheet(context: context, message: message),
@@ -1262,15 +1270,24 @@ class MessageWithPossibleSender extends StatelessWidget {
12621270
if (senderRow != null)
12631271
Padding(padding: const EdgeInsets.fromLTRB(16, 2, 16, 0),
12641272
child: senderRow),
1265-
EditStateMarker(
1266-
editState: message.editState,
1273+
Row(
1274+
crossAxisAlignment: CrossAxisAlignment.baseline,
1275+
textBaseline: localizedTextBaseline(context),
12671276
children: [
1277+
const SizedBox(width: 16),
12681278
Expanded(child: Column(
12691279
crossAxisAlignment: CrossAxisAlignment.stretch,
12701280
children: [
12711281
MessageContent(message: message, content: item.content),
12721282
if ((message.reactions?.total ?? 0) > 0)
1273-
ReactionChipsList(messageId: message.id, reactions: message.reactions!)
1283+
ReactionChipsList(messageId: message.id, reactions: message.reactions!),
1284+
if (editStateText != null)
1285+
Text(editStateText, textAlign: TextAlign.end,
1286+
style: TextStyle(
1287+
color: messageListTheme.editStateLabel,
1288+
fontFamily: 'Source Sans 3',
1289+
fontSize: 12,
1290+
height: (12 / 12))),
12741291
])),
12751292
SizedBox(width: 16,
12761293
child: message.flags.contains(MessageFlag.starred)

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';
@@ -1037,10 +1036,10 @@ void main() {
10371036
});
10381037
});
10391038

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

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

11391072
group('_UnreadMarker animations', () {

0 commit comments

Comments
 (0)