Skip to content

Commit fe4655f

Browse files
gnpricechrisbobbe
authored andcommitted
msglist: Show date separators
Fixes: #173
1 parent 69767fb commit fe4655f

File tree

3 files changed

+71
-8
lines changed

3 files changed

+71
-8
lines changed

lib/model/message_list.dart

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ class MessageListRecipientHeaderItem extends MessageListItem {
2525
MessageListRecipientHeaderItem(this.message);
2626
}
2727

28+
class MessageListDateSeparatorItem extends MessageListItem {
29+
final Message message;
30+
31+
MessageListDateSeparatorItem(this.message);
32+
}
33+
2834
/// A message to show in the message list.
2935
class MessageListMessageItem extends MessageListItem {
3036
final Message message;
@@ -118,6 +124,7 @@ mixin _MessageSequence {
118124
case MessageListDirection.older: return -1;
119125
}
120126
case MessageListRecipientHeaderItem(:var message):
127+
case MessageListDateSeparatorItem(:var message):
121128
return (message.id <= messageId) ? -1 : 1;
122129
case MessageListMessageItem(:var message): return message.id.compareTo(messageId);
123130
}
@@ -181,18 +188,19 @@ mixin _MessageSequence {
181188
final message = messages[index];
182189
final content = contents[index];
183190
bool canShareSender;
184-
if (index > 0
185-
&& haveSameRecipient(messages[index - 1], message)
186-
&& messagesSameDay(messages[index - 1], message)) {
191+
if (index == 0 || !haveSameRecipient(messages[index - 1], message)) {
192+
items.add(MessageListRecipientHeaderItem(message));
193+
canShareSender = false;
194+
} else if (!messagesSameDay(messages[index - 1], message)) {
195+
items.add(MessageListDateSeparatorItem(message));
196+
canShareSender = false;
197+
} else {
187198
assert(items.last is MessageListMessageItem);
188199
final prevMessageItem = items.last as MessageListMessageItem;
189200
assert(identical(prevMessageItem.message, messages[index - 1]));
190201
assert(prevMessageItem.isLastInBlock);
191202
prevMessageItem.isLastInBlock = false;
192203
canShareSender = (prevMessageItem.message.senderId == message.senderId);
193-
} else {
194-
items.add(MessageListRecipientHeaderItem(message));
195-
canShareSender = false;
196204
}
197205
items.add(MessageListMessageItem(message, content,
198206
showSender: !canShareSender, isLastInBlock: true));

lib/widgets/message_list.dart

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,11 @@ class _MessageListState extends State<MessageList> with PerAccountStoreAwareStat
344344
final header = RecipientHeader(message: data.message, narrow: widget.narrow);
345345
return StickyHeaderItem(allowOverflow: true,
346346
header: header, child: header);
347+
case MessageListDateSeparatorItem():
348+
final header = RecipientHeader(message: data.message, narrow: widget.narrow);
349+
return StickyHeaderItem(allowOverflow: true,
350+
header: header,
351+
child: DateSeparator(message: data.message));
347352
case MessageListMessageItem():
348353
final header = RecipientHeader(message: data.message, narrow: widget.narrow);
349354
return MessageItem(
@@ -463,6 +468,50 @@ class RecipientHeader extends StatelessWidget {
463468
}
464469
}
465470

471+
class DateSeparator extends StatelessWidget {
472+
const DateSeparator({super.key, required this.message});
473+
474+
final Message message;
475+
476+
// This color matches recipient headers. TODO(design) is that what we want?
477+
static final _textColor = Colors.black.withOpacity(0.4);
478+
479+
@override
480+
Widget build(BuildContext context) {
481+
// This makes the small-caps text vertically centered,
482+
// to align with the vertically centered divider lines.
483+
const textBottomPadding = 2.0;
484+
485+
return ColoredBox(color: Colors.white,
486+
child: Padding(
487+
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 2),
488+
child: Row(children: [
489+
const Expanded(
490+
child: SizedBox(height: 0,
491+
child: DecoratedBox(
492+
decoration: BoxDecoration(
493+
border: Border(
494+
bottom: BorderSide(
495+
width: 0,
496+
color: Colors.black)))))),
497+
Padding(padding: const EdgeInsets.fromLTRB(2, 0, 2, textBottomPadding),
498+
child: DateText(
499+
color: _textColor,
500+
fontSize: 16,
501+
height: (16 / 16),
502+
timestamp: message.timestamp)),
503+
const SizedBox(height: 0, width: 12,
504+
child: DecoratedBox(
505+
decoration: BoxDecoration(
506+
border: Border(
507+
bottom: BorderSide(
508+
width: 0,
509+
color: Colors.black)))))
510+
])),
511+
);
512+
}
513+
}
514+
466515
class MessageItem extends StatelessWidget {
467516
const MessageItem({
468517
super.key,

test/model/message_list_test.dart

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -948,11 +948,13 @@ void checkInvariants(MessageListView model) {
948948
for (int j = 0; j < model.messages.length; j++) {
949949
bool isFirstInBlock = false;
950950
if (j == 0
951-
|| !haveSameRecipient(model.messages[j-1], model.messages[j])
952-
|| !messagesSameDay(model.messages[j-1], model.messages[j])) {
951+
|| !haveSameRecipient(model.messages[j-1], model.messages[j])) {
953952
check(model.items[i++]).isA<MessageListRecipientHeaderItem>()
954953
.message.identicalTo(model.messages[j]);
955954
isFirstInBlock = true;
955+
} else if (!messagesSameDay(model.messages[j-1], model.messages[j])) {
956+
check(model.items[i++]).isA<MessageListDateSeparatorItem>()
957+
.message.identicalTo(model.messages[j]);
956958
}
957959
check(model.items[i++]).isA<MessageListMessageItem>()
958960
..message.identicalTo(model.messages[j])
@@ -969,6 +971,10 @@ extension MessageListRecipientHeaderItemChecks on Subject<MessageListRecipientHe
969971
Subject<Message> get message => has((x) => x.message, 'message');
970972
}
971973

974+
extension MessageListDateSeparatorItemChecks on Subject<MessageListDateSeparatorItem> {
975+
Subject<Message> get message => has((x) => x.message, 'message');
976+
}
977+
972978
extension MessageListMessageItemChecks on Subject<MessageListMessageItem> {
973979
Subject<Message> get message => has((x) => x.message, 'message');
974980
Subject<ZulipContent> get content => has((x) => x.content, 'content');

0 commit comments

Comments
 (0)