Skip to content

Commit 2a7a927

Browse files
semantics: Fix content navigation order in message content when links present
Fixes #1963.
1 parent 1699aa5 commit 2a7a927

File tree

2 files changed

+106
-101
lines changed

2 files changed

+106
-101
lines changed

lib/widgets/content.dart

Lines changed: 58 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -295,13 +295,14 @@ class MessageContent extends StatelessWidget {
295295
@override
296296
Widget build(BuildContext context) {
297297
final content = this.content;
298-
return InheritedMessage(message: message,
299-
child: DefaultTextStyle(
300-
style: ContentTheme.of(context).textStylePlainParagraph,
301-
child: switch (content) {
302-
ZulipContent() => BlockContentList(nodes: content.nodes),
303-
PollContent() => PollWidget(messageId: message.id, poll: content.poll),
304-
}));
298+
return MergeSemantics(
299+
child: InheritedMessage(message: message,
300+
child: DefaultTextStyle(
301+
style: ContentTheme.of(context).textStylePlainParagraph,
302+
child: switch (content) {
303+
ZulipContent() => BlockContentList(nodes: content.nodes),
304+
PollContent() => PollWidget(messageId: message.id, poll: content.poll),
305+
})));
305306
}
306307
}
307308

@@ -333,53 +334,56 @@ class BlockContentList extends StatelessWidget {
333334

334335
@override
335336
Widget build(BuildContext context) {
336-
return Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
337-
...nodes.map((node) {
338-
return switch (node) {
339-
LineBreakNode() =>
340-
// This goes in a Column. So to get the effect of a newline,
341-
// just use an empty Text.
342-
const Text(''),
343-
ThematicBreakNode() => const ThematicBreak(),
344-
ParagraphNode() => Paragraph(node: node),
345-
HeadingNode() => Heading(node: node),
346-
QuotationNode() => Quotation(node: node),
347-
ListNode() => ListNodeWidget(node: node),
348-
SpoilerNode() => Spoiler(node: node),
349-
CodeBlockNode() => CodeBlock(node: node),
350-
MathBlockNode() => MathBlock(node: node),
351-
ImagePreviewNodeList() => MessageImagePreviewList(node: node),
352-
ImagePreviewNode() => (){
353-
assert(false,
354-
"[ImagePreviewNode] not allowed in [BlockContentList]. "
355-
"It should be wrapped in [ImagePreviewNodeList]."
356-
);
357-
return MessageImagePreview(node: node);
358-
}(),
359-
InlineVideoNode() => MessageInlineVideo(node: node),
360-
EmbedVideoNode() => MessageEmbedVideo(node: node),
361-
TableNode() => MessageTable(node: node),
362-
TableRowNode() => () {
363-
assert(false,
364-
"[TableRowNode] not allowed in [BlockContentList]. "
365-
"It should be wrapped in [TableNode]."
366-
);
367-
return const SizedBox.shrink();
368-
}(),
369-
TableCellNode() => () {
370-
assert(false,
371-
"[TableCellNode] not allowed in [BlockContentList]. "
372-
"It should be wrapped in [TableRowNode]."
373-
);
374-
return const SizedBox.shrink();
375-
}(),
376-
WebsitePreviewNode() => WebsitePreview(node: node),
377-
UnimplementedBlockContentNode() =>
378-
Text.rich(_errorUnimplemented(node, context: context)),
379-
};
380-
381-
}),
382-
]);
337+
return Semantics(
338+
explicitChildNodes: true,
339+
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
340+
...nodes.map((node) {
341+
return switch (node) {
342+
LineBreakNode() =>
343+
// This goes in a Column. So to get the effect of a newline,
344+
// just use an empty Text.
345+
const Text(''),
346+
ThematicBreakNode() => const ThematicBreak(),
347+
ParagraphNode() => Paragraph(node: node),
348+
HeadingNode() => Heading(node: node),
349+
QuotationNode() => Quotation(node: node),
350+
ListNode() => ListNodeWidget(node: node),
351+
SpoilerNode() => Spoiler(node: node),
352+
CodeBlockNode() => CodeBlock(node: node),
353+
MathBlockNode() => MathBlock(node: node),
354+
ImagePreviewNodeList() => MessageImagePreviewList(node: node),
355+
ImagePreviewNode() => (){
356+
assert(false,
357+
"[ImagePreviewNode] not allowed in [BlockContentList]. "
358+
"It should be wrapped in [ImagePreviewNodeList]."
359+
);
360+
return MessageImagePreview(node: node);
361+
}(),
362+
InlineVideoNode() => MessageInlineVideo(node: node),
363+
EmbedVideoNode() => MessageEmbedVideo(node: node),
364+
TableNode() => MessageTable(node: node),
365+
TableRowNode() => () {
366+
assert(false,
367+
"[TableRowNode] not allowed in [BlockContentList]. "
368+
"It should be wrapped in [TableNode]."
369+
);
370+
return const SizedBox.shrink();
371+
}(),
372+
TableCellNode() => () {
373+
assert(false,
374+
"[TableCellNode] not allowed in [BlockContentList]. "
375+
"It should be wrapped in [TableRowNode]."
376+
);
377+
return const SizedBox.shrink();
378+
}(),
379+
WebsitePreviewNode() => WebsitePreview(node: node),
380+
UnimplementedBlockContentNode() =>
381+
Text.rich(_errorUnimplemented(node, context: context)),
382+
};
383+
384+
}),
385+
]),
386+
);
383387
}
384388
}
385389

lib/widgets/message_list.dart

Lines changed: 48 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2205,53 +2205,54 @@ class MessageWithPossibleSender extends StatelessWidget {
22052205
: () => showMessageActionSheet(context: context, message: message),
22062206
child: Padding(
22072207
padding: const EdgeInsets.only(top: 4),
2208-
child: Column(children: [
2209-
if (item.showSender)
2210-
SenderRow(message: message,
2211-
timestampStyle: MessageTimestampStyle.timeOnly),
2212-
Row(
2213-
crossAxisAlignment: CrossAxisAlignment.baseline,
2214-
textBaseline: localizedTextBaseline(context),
2215-
children: [
2216-
const SizedBox(width: 16),
2217-
Expanded(child: showAsMuted
2218-
? Align(
2219-
alignment: AlignmentDirectional.topStart,
2220-
child: ZulipWebUiKitButton(
2221-
label: zulipLocalizations.revealButtonLabel,
2222-
icon: ZulipIcons.eye,
2223-
size: ZulipWebUiKitButtonSize.small,
2224-
intent: ZulipWebUiKitButtonIntent.neutral,
2225-
attention: ZulipWebUiKitButtonAttention.minimal,
2226-
onPressed: () {
2227-
MessageListPage.ancestorOf(context).revealMutedMessage(message.id);
2228-
}))
2229-
: Column(
2230-
crossAxisAlignment: CrossAxisAlignment.stretch,
2231-
children: [
2232-
content,
2233-
if ((message.reactions?.total ?? 0) > 0)
2234-
ReactionChipsList(messageId: message.id, reactions: message.reactions!),
2235-
if (editMessageErrorStatus != null)
2236-
_EditMessageStatusRow(messageId: message.id, status: editMessageErrorStatus)
2237-
else if (editStateText != null)
2238-
Padding(
2239-
padding: const EdgeInsets.only(bottom: 4),
2240-
child: Text(editStateText,
2241-
textAlign: TextAlign.end,
2242-
style: TextStyle(
2243-
color: designVariables.labelEdited,
2244-
fontSize: 12,
2245-
height: (12 / 12),
2246-
letterSpacing: proportionalLetterSpacing(context,
2247-
0.05, baseFontSize: 12))))
2248-
else
2249-
Padding(padding: const EdgeInsets.only(bottom: 4))
2250-
])),
2251-
SizedBox(width: 16,
2252-
child: star),
2253-
]),
2254-
])));
2208+
child: MergeSemantics(
2209+
child: Column(children: [
2210+
if (item.showSender)
2211+
SenderRow(message: message,
2212+
timestampStyle: MessageTimestampStyle.timeOnly),
2213+
Row(
2214+
crossAxisAlignment: CrossAxisAlignment.baseline,
2215+
textBaseline: localizedTextBaseline(context),
2216+
children: [
2217+
const SizedBox(width: 16),
2218+
Expanded(child: showAsMuted
2219+
? Align(
2220+
alignment: AlignmentDirectional.topStart,
2221+
child: ZulipWebUiKitButton(
2222+
label: zulipLocalizations.revealButtonLabel,
2223+
icon: ZulipIcons.eye,
2224+
size: ZulipWebUiKitButtonSize.small,
2225+
intent: ZulipWebUiKitButtonIntent.neutral,
2226+
attention: ZulipWebUiKitButtonAttention.minimal,
2227+
onPressed: () {
2228+
MessageListPage.ancestorOf(context).revealMutedMessage(message.id);
2229+
}))
2230+
: Column(
2231+
crossAxisAlignment: CrossAxisAlignment.stretch,
2232+
children: [
2233+
content,
2234+
if ((message.reactions?.total ?? 0) > 0)
2235+
ReactionChipsList(messageId: message.id, reactions: message.reactions!),
2236+
if (editMessageErrorStatus != null)
2237+
_EditMessageStatusRow(messageId: message.id, status: editMessageErrorStatus)
2238+
else if (editStateText != null)
2239+
Padding(
2240+
padding: const EdgeInsets.only(bottom: 4),
2241+
child: Text(editStateText,
2242+
textAlign: TextAlign.end,
2243+
style: TextStyle(
2244+
color: designVariables.labelEdited,
2245+
fontSize: 12,
2246+
height: (12 / 12),
2247+
letterSpacing: proportionalLetterSpacing(context,
2248+
0.05, baseFontSize: 12))))
2249+
else
2250+
Padding(padding: const EdgeInsets.only(bottom: 4))
2251+
])),
2252+
SizedBox(width: 16,
2253+
child: star),
2254+
]),
2255+
]))));
22552256
}
22562257
}
22572258

0 commit comments

Comments
 (0)