Skip to content

Commit 736cd84

Browse files
committed
content: Handle <hr> horizontal lines
Fixes: zulip#353
1 parent 43561bf commit 736cd84

File tree

4 files changed

+54
-0
lines changed

4 files changed

+54
-0
lines changed

lib/model/content.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,19 @@ class LineBreakNode extends BlockContentNode {
178178
int get hashCode => 'LineBreakNode'.hashCode;
179179
}
180180

181+
/// A `hr` element
182+
class ThematicBreakNode extends BlockContentNode {
183+
const ThematicBreakNode({super.debugHtmlNode});
184+
185+
@override
186+
bool operator ==(Object other) {
187+
return other is ThematicBreakNode;
188+
}
189+
190+
@override
191+
int get hashCode => 'ThematicBreakNode'.hashCode;
192+
}
193+
181194
/// A `p` element, or a place where the DOM tree logically wanted one.
182195
///
183196
/// We synthesize these in the absence of an actual `p` element in cases where
@@ -969,6 +982,10 @@ class _ZulipContentParser {
969982
return LineBreakNode(debugHtmlNode: debugHtmlNode);
970983
}
971984

985+
if (localName == 'hr' && className.isEmpty) {
986+
return ThematicBreakNode(debugHtmlNode: debugHtmlNode);
987+
}
988+
972989
if (localName == 'p' && className.isEmpty) {
973990
// Oddly, the way a math block gets encoded in Zulip HTML is inside a <p>.
974991
if (element.nodes case [dom.Element(localName: 'span') && var child, ...]) {

lib/widgets/content.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ class BlockContentList extends StatelessWidget {
7474
// This goes in a Column. So to get the effect of a newline,
7575
// just use an empty Text.
7676
return const Text('');
77+
} else if (node is ThematicBreakNode) {
78+
return const ThematicBreak();
7779
} else if (node is ParagraphNode) {
7880
return Paragraph(node: node);
7981
} else if (node is HeadingNode) {
@@ -107,6 +109,22 @@ class BlockContentList extends StatelessWidget {
107109
}
108110
}
109111

112+
class ThematicBreak extends StatelessWidget {
113+
const ThematicBreak({super.key});
114+
115+
static const htmlHeight = 2.0;
116+
static const htmlMarginY = 20.0;
117+
118+
@override
119+
Widget build(BuildContext context) {
120+
return Divider(
121+
color: const HSLColor.fromAHSL(1, 0, 0, .87).toColor(),
122+
thickness: htmlHeight,
123+
height: 2 * htmlMarginY + htmlHeight,
124+
);
125+
}
126+
}
127+
110128
class Paragraph extends StatelessWidget {
111129
const Paragraph({super.key, required this.node});
112130

test/model/content_test.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,16 @@ class ContentExample {
521521
blockUnimplemented('more text'),
522522
]]),
523523
]);
524+
525+
static const thematicBreaks = ContentExample(
526+
'parse thematic break (<hr>) in block context',
527+
'a\n---\nb',
528+
'<p>a</p>\n<hr>\n<p>b</p>',
529+
[
530+
ParagraphNode(links: null, nodes: [TextNode('a')]),
531+
ThematicBreakNode(),
532+
ParagraphNode(links: null, nodes: [TextNode('b')]),
533+
]);
524534
}
525535

526536
UnimplementedBlockContentNode blockUnimplemented(String html) {
@@ -721,6 +731,8 @@ void main() {
721731
LineBreakNode(),
722732
]);
723733

734+
testParseExample(ContentExample.thematicBreaks);
735+
724736
testParse('parse two plain-text paragraphs',
725737
// "hello\n\nworld"
726738
'<p>hello</p>\n<p>world</p>', const [

test/widgets/content_test.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ void main() {
8181
});
8282
}
8383

84+
group('ThematicBreak', () {
85+
testWidgets('smoke ThematicBreak', (tester) async {
86+
await prepareContentBare(tester, ContentExample.thematicBreaks.html);
87+
tester.widget(find.byType(ThematicBreak));
88+
});
89+
});
90+
8491
group('Heading', () {
8592
testWidgets('plain h6', (tester) async {
8693
await prepareContentBare(tester,

0 commit comments

Comments
 (0)