Skip to content

Commit 598bb7d

Browse files
committed
content: Start rendering poll content
Fixes: zulip#165 Signed-off-by: Zixuan James Li <[email protected]>
1 parent 65f4626 commit 598bb7d

File tree

5 files changed

+61
-0
lines changed

5 files changed

+61
-0
lines changed

lib/model/content.dart

+11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart';
33
import 'package:html/dom.dart' as dom;
44
import 'package:html/parser.dart';
55

6+
import '../api/model/submessage.dart';
67
import 'code_block.dart';
78

89
/// A node in a parse tree for Zulip message-style content.
@@ -75,6 +76,16 @@ mixin UnimplementedNode on ContentNode {
7576
/// A parsed, ready-to-render representation of Zulip message content.
7677
sealed class ZulipMessageContent {}
7778

79+
/// A wrapper around a mutable representation of a Zulip poll message.
80+
///
81+
/// Consumers are expected to listen for [Poll]'s changes to receive
82+
/// live-updates.
83+
class PollContent implements ZulipMessageContent {
84+
const PollContent(this.poll);
85+
86+
final Poll poll;
87+
}
88+
7889
/// A complete parse tree for a Zulip message's content,
7990
/// or other complete piece of Zulip HTML content.
8091
///

lib/model/message_list.dart

+2
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ mixin _MessageSequence {
135135
}
136136

137137
ZulipMessageContent _parseMessageContent(Message message) {
138+
final poll = message.poll;
139+
if (poll != null) return PollContent(poll);
138140
return parseContent(message.content);
139141
}
140142

lib/widgets/content.dart

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import 'dialog.dart';
1818
import 'icons.dart';
1919
import 'lightbox.dart';
2020
import 'message_list.dart';
21+
import 'poll.dart';
2122
import 'store.dart';
2223
import 'text.dart';
2324

@@ -263,6 +264,7 @@ class MessageContent extends StatelessWidget {
263264
style: ContentTheme.of(context).textStylePlainParagraph,
264265
child: switch (content) {
265266
ZulipContent() => BlockContentList(nodes: content.nodes),
267+
PollContent() => PollWidget(poll: content.poll),
266268
}));
267269
}
268270
}

test/model/content_checks.dart

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import 'package:checks/checks.dart';
12
import 'package:checks/context.dart';
23
import 'package:flutter/foundation.dart';
4+
import 'package:zulip/api/model/submessage.dart';
35
import 'package:zulip/model/content.dart';
46

57
extension ContentNodeChecks on Subject<ContentNode> {
@@ -115,3 +117,7 @@ Iterable<LinkNode> _findLinkNodes(Iterable<InlineContentNode> nodes) {
115117
return _findLinkNodes(node.nodes);
116118
});
117119
}
120+
121+
extension PollContentChecks on Subject<PollContent> {
122+
Subject<Poll> get poll => has((x) => x.poll, 'poll');
123+
}

test/model/message_list_test.dart

+40
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,41 @@ void main() {
545545
checkNotNotified();
546546
check(model).fetched.isFalse();
547547
});
548+
549+
test('parse ZulipContent', () async {
550+
final stream = eg.stream();
551+
await prepare(narrow: ChannelNarrow(stream.streamId));
552+
await prepareMessages(foundOldest: true, messages: []);
553+
554+
await store.handleEvent(MessageEvent(id: 0,
555+
message: eg.streamMessage(stream: stream)));
556+
// Each [checkNotifiedOnce] call ensures there's been a [checkInvariants]
557+
// call, where the [ContentNode] gets checked. The additional checks to
558+
// make this test explicit.
559+
checkNotifiedOnce();
560+
check(model).messages.single.poll.isNull();
561+
check(model).contents.single.isA<ZulipContent>();
562+
});
563+
564+
test('parse PollContent', () async {
565+
final stream = eg.stream();
566+
await prepare(narrow: ChannelNarrow(stream.streamId));
567+
await prepareMessages(foundOldest: true, messages: []);
568+
569+
await store.handleEvent(MessageEvent(id: 0, message: eg.streamMessage(
570+
stream: stream,
571+
sender: eg.selfUser,
572+
submessages: [
573+
eg.submessage(senderId: eg.selfUser.userId,
574+
content: eg.pollWidgetData(question: 'question', options: ['A'])),
575+
])));
576+
// Each [checkNotifiedOnce] call ensures there's been a [checkInvariants]
577+
// call, where the value of the [Poll] gets checked. The additional
578+
// checks make this test explicit.
579+
checkNotifiedOnce();
580+
check(model).messages.single.poll.isNotNull();
581+
check(model).contents.single.isA<PollContent>();
582+
});
548583
});
549584

550585
group('messageContentChanged', () {
@@ -1744,6 +1779,11 @@ void checkInvariants(MessageListView model) {
17441779

17451780
check(model).contents.length.equals(model.messages.length);
17461781
for (int i = 0; i < model.contents.length; i++) {
1782+
final poll = model.messages[i].poll;
1783+
if (poll != null) {
1784+
check(model).contents[i].isA<PollContent>().poll.identicalTo(poll);
1785+
continue;
1786+
}
17471787
check(model.contents[i]).isA<ZulipContent>()
17481788
.equalsNode(parseContent(model.messages[i].content));
17491789
}

0 commit comments

Comments
 (0)