Skip to content

Commit a164073

Browse files
committed
content: Extract a superclass for ZulipContent
This prepares for a later change that introduces new content types like polls and todos. Signed-off-by: Zixuan James Li <[email protected]>
1 parent 5d36977 commit a164073

File tree

4 files changed

+25
-17
lines changed

4 files changed

+25
-17
lines changed

lib/model/content.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,15 @@ mixin UnimplementedNode on ContentNode {
7272
}
7373
}
7474

75+
sealed class ZulipMessageContent {}
76+
7577
/// A complete parse tree for a Zulip message's content,
7678
/// or other complete piece of Zulip HTML content.
7779
///
7880
/// This is a parsed representation for an entire value of [Message.content],
7981
/// [Stream.renderedDescription], or other text from a Zulip server that comes
8082
/// in the same Zulip HTML format.
81-
class ZulipContent extends ContentNode {
83+
class ZulipContent extends ContentNode implements ZulipMessageContent {
8284
const ZulipContent({super.debugHtmlNode, required this.nodes});
8385

8486
final List<BlockContentNode> nodes;

lib/model/message_list.dart

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class MessageListDateSeparatorItem extends MessageListItem {
3535
/// A message to show in the message list.
3636
class MessageListMessageItem extends MessageListItem {
3737
final Message message;
38-
ZulipContent content;
38+
ZulipMessageContent content;
3939
bool showSender;
4040
bool isLastInBlock;
4141

@@ -98,7 +98,7 @@ mixin _MessageSequence {
9898
///
9999
/// This information is completely derived from [messages].
100100
/// It exists as an optimization, to memoize the work of parsing.
101-
final List<ZulipContent> contents = [];
101+
final List<ZulipMessageContent> contents = [];
102102

103103
/// The messages and their siblings in the UI, in order.
104104
///
@@ -134,10 +134,14 @@ mixin _MessageSequence {
134134
}
135135
}
136136

137+
ZulipMessageContent _parseMessageContent(Message message) {
138+
return parseContent(message.content);
139+
}
140+
137141
/// Update data derived from the content of the index-th message.
138142
void _reparseContent(int index) {
139143
final message = messages[index];
140-
final content = parseContent(message.content);
144+
final content = _parseMessageContent(message);
141145
contents[index] = content;
142146

143147
final itemIndex = findItemWithMessageId(message.id);
@@ -154,7 +158,7 @@ mixin _MessageSequence {
154158
void _addMessage(Message message) {
155159
assert(contents.length == messages.length);
156160
messages.add(message);
157-
contents.add(parseContent(message.content));
161+
contents.add(_parseMessageContent(message));
158162
assert(contents.length == messages.length);
159163
_processMessage(messages.length - 1);
160164
}
@@ -197,7 +201,7 @@ mixin _MessageSequence {
197201
/// If none of [messageIds] are found, this is a no-op.
198202
bool _removeMessagesById(Iterable<int> messageIds) {
199203
final messagesToRemoveById = <int>{};
200-
final contentToRemove = Set<ZulipContent>.identity();
204+
final contentToRemove = Set<ZulipMessageContent>.identity();
201205
for (final messageId in messageIds) {
202206
final index = _findMessageWithId(messageId);
203207
if (index == -1) continue;
@@ -223,7 +227,7 @@ mixin _MessageSequence {
223227
assert(contents.length == messages.length);
224228
messages.insertAll(index, toInsert);
225229
contents.insertAll(index, toInsert.map(
226-
(message) => parseContent(message.content)));
230+
(message) => _parseMessageContent(message)));
227231
assert(contents.length == messages.length);
228232
_reprocessAll();
229233
}
@@ -243,7 +247,7 @@ mixin _MessageSequence {
243247
void _recompute() {
244248
assert(contents.length == messages.length);
245249
contents.clear();
246-
contents.addAll(messages.map((message) => parseContent(message.content)));
250+
contents.addAll(messages.map((message) => _parseMessageContent(message)));
247251
assert(contents.length == messages.length);
248252
_reprocessAll();
249253
}

lib/widgets/content.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,17 +222,19 @@ const double kBaseFontSize = 17;
222222
/// This does not include metadata like the sender's name and avatar, the time,
223223
/// or the message's status as starred or edited.
224224
class MessageContent extends StatelessWidget {
225-
const MessageContent({super.key, required this.message, required this.content});
225+
const MessageContent({super.key, required this.message, required ZulipMessageContent content}) : _content = content;
226226

227227
final Message message;
228-
final ZulipContent content;
228+
final ZulipMessageContent _content;
229229

230230
@override
231231
Widget build(BuildContext context) {
232232
return InheritedMessage(message: message,
233233
child: DefaultTextStyle(
234234
style: ContentTheme.of(context).textStylePlainParagraph,
235-
child: BlockContentList(nodes: content.nodes)));
235+
child: switch(_content) {
236+
ZulipContent() => BlockContentList(nodes: _content.nodes),
237+
}));
236238
}
237239
}
238240

test/model/message_list_test.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,12 +1257,12 @@ void main() {
12571257
model.contents[0] = const ZulipContent(nodes: [
12581258
ParagraphNode(links: null, nodes: [TextNode('something outdated')])
12591259
]);
1260-
check(model.contents[0]).not((it) => it.equalsNode(correctContent));
1260+
check(model.contents[0]).isA<ZulipContent>().not((it) => it.equalsNode(correctContent));
12611261

12621262
model.reassemble();
12631263
checkNotifiedOnce();
12641264
check(model).messages.length.equals(31);
1265-
check(model.contents[0]).equalsNode(correctContent);
1265+
check(model.contents[0]).isA<ZulipContent>().equalsNode(correctContent);
12661266
});
12671267

12681268
group('stream/topic muting', () {
@@ -1740,7 +1740,7 @@ void checkInvariants(MessageListView model) {
17401740

17411741
check(model).contents.length.equals(model.messages.length);
17421742
for (int i = 0; i < model.contents.length; i++) {
1743-
check(model.contents[i])
1743+
check(model.contents[i]).isA<ZulipContent>()
17441744
.equalsNode(parseContent(model.messages[i].content));
17451745
}
17461746

@@ -1765,7 +1765,7 @@ void checkInvariants(MessageListView model) {
17651765
}
17661766
check(model.items[i++]).isA<MessageListMessageItem>()
17671767
..message.identicalTo(model.messages[j])
1768-
..content.identicalTo(model.contents[j])
1768+
..content.identicalTo(model.contents[j] as ZulipContent)
17691769
..showSender.equals(
17701770
forcedShowSender || model.messages[j].senderId != model.messages[j-1].senderId)
17711771
..isLastInBlock.equals(
@@ -1790,7 +1790,7 @@ extension MessageListDateSeparatorItemChecks on Subject<MessageListDateSeparator
17901790

17911791
extension MessageListMessageItemChecks on Subject<MessageListMessageItem> {
17921792
Subject<Message> get message => has((x) => x.message, 'message');
1793-
Subject<ZulipContent> get content => has((x) => x.content, 'content');
1793+
Subject<ZulipMessageContent> get content => has((x) => x.content, 'content');
17941794
Subject<bool> get showSender => has((x) => x.showSender, 'showSender');
17951795
Subject<bool> get isLastInBlock => has((x) => x.isLastInBlock, 'isLastInBlock');
17961796
}
@@ -1799,7 +1799,7 @@ extension MessageListViewChecks on Subject<MessageListView> {
17991799
Subject<PerAccountStore> get store => has((x) => x.store, 'store');
18001800
Subject<Narrow> get narrow => has((x) => x.narrow, 'narrow');
18011801
Subject<List<Message>> get messages => has((x) => x.messages, 'messages');
1802-
Subject<List<ZulipContent>> get contents => has((x) => x.contents, 'contents');
1802+
Subject<List<ZulipMessageContent>> get contents => has((x) => x.contents, 'contents');
18031803
Subject<List<MessageListItem>> get items => has((x) => x.items, 'items');
18041804
Subject<bool> get fetched => has((x) => x.fetched, 'fetched');
18051805
Subject<bool> get haveOldest => has((x) => x.haveOldest, 'haveOldest');

0 commit comments

Comments
 (0)