chore: only show emoji button for save-vocab tokens, fix alignment for non-token text (#4071)

This commit is contained in:
ggurdin 2025-09-22 11:33:03 -04:00 committed by GitHub
parent d89d852488
commit 26a3a03ad6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 114 additions and 72 deletions

View file

@ -124,6 +124,7 @@ class HtmlMessage extends StatelessWidget {
'tg-forward',
// #Pangea
'token',
'nontoken',
// Pangea#
};
@ -255,6 +256,12 @@ class HtmlMessage extends StatelessWidget {
position = substringIndex;
}
for (int i = 0; i < result.length; i++) {
if (!result[i].startsWith('<') && !result[i].endsWith('>')) {
result[i] = '<nontoken>${result[i]}</nontoken>';
}
}
if (pangeaMessageEvent?.textDirection == TextDirection.rtl) {
for (int i = 0; i < result.length; i++) {
final tag = result[i];
@ -913,48 +920,74 @@ class HtmlMessage extends StatelessWidget {
'sub' => const TextStyle(fontFeatures: [FontFeature.subscripts()]),
_ => null,
};
// Pangea#
return TextSpan(
style: switch (node.localName) {
'body' => TextStyle(
fontSize: fontSize,
color: textColor,
return WidgetSpan(
alignment: readingAssistanceMode == ReadingAssistanceMode.practiceMode
? PlaceholderAlignment.bottom
: PlaceholderAlignment.middle,
child: Column(
children: [
if (node.localName == 'nontoken' &&
overlayController?.selectedMode == SelectMode.emoji)
TokenEmojiButton(
token: null,
eventId: event.eventId,
),
RichText(
text: TextSpan(
style: style,
children: _renderWithLineBreaks(
node.nodes,
context,
textStyle.merge(style ?? const TextStyle()),
depth: depth,
),
),
),
'a' => linkStyle,
'strong' => const TextStyle(fontWeight: FontWeight.bold),
'em' || 'i' => const TextStyle(fontStyle: FontStyle.italic),
'del' ||
's' ||
'strikethrough' =>
const TextStyle(decoration: TextDecoration.lineThrough),
'u' => const TextStyle(decoration: TextDecoration.underline),
'h1' => TextStyle(fontSize: fontSize * 1.6, height: 2),
'h2' => TextStyle(fontSize: fontSize * 1.5, height: 2),
'h3' => TextStyle(fontSize: fontSize * 1.4, height: 2),
'h4' => TextStyle(fontSize: fontSize * 1.3, height: 1.75),
'h5' => TextStyle(fontSize: fontSize * 1.2, height: 1.75),
'h6' => TextStyle(fontSize: fontSize * 1.1, height: 1.5),
'span' => TextStyle(
color: node.attributes['color']?.hexToColor ??
node.attributes['data-mx-color']?.hexToColor ??
textColor,
backgroundColor:
node.attributes['data-mx-bg-color']?.hexToColor,
),
'sup' =>
const TextStyle(fontFeatures: [FontFeature.superscripts()]),
'sub' => const TextStyle(fontFeatures: [FontFeature.subscripts()]),
_ => null,
},
children: _renderWithLineBreaks(
node.nodes,
context,
// #Pangea
textStyle.merge(style ?? const TextStyle()),
// Pangea#
depth: depth,
],
),
);
// return TextSpan(
// style: switch (node.localName) {
// 'body' => TextStyle(
// fontSize: fontSize,
// color: textColor,
// ),
// 'a' => linkStyle,
// 'strong' => const TextStyle(fontWeight: FontWeight.bold),
// 'em' || 'i' => const TextStyle(fontStyle: FontStyle.italic),
// 'del' ||
// 's' ||
// 'strikethrough' =>
// const TextStyle(decoration: TextDecoration.lineThrough),
// 'u' => const TextStyle(decoration: TextDecoration.underline),
// 'h1' => TextStyle(fontSize: fontSize * 1.6, height: 2),
// 'h2' => TextStyle(fontSize: fontSize * 1.5, height: 2),
// 'h3' => TextStyle(fontSize: fontSize * 1.4, height: 2),
// 'h4' => TextStyle(fontSize: fontSize * 1.3, height: 1.75),
// 'h5' => TextStyle(fontSize: fontSize * 1.2, height: 1.75),
// 'h6' => TextStyle(fontSize: fontSize * 1.1, height: 1.5),
// 'span' => TextStyle(
// color: node.attributes['color']?.hexToColor ??
// node.attributes['data-mx-color']?.hexToColor ??
// textColor,
// backgroundColor:
// node.attributes['data-mx-bg-color']?.hexToColor,
// ),
// 'sup' => const TextStyle(
// fontFeatures: [FontFeature.superscripts()],
// ),
// 'sub' => const TextStyle(
// fontFeatures: [FontFeature.subscripts()],
// ),
// _ => null,
// },
// children: _renderWithLineBreaks(
// node.nodes,
// context,
// depth: depth,
// ),
// );
// Pangea#
}
}

View file

@ -5,17 +5,17 @@ import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/widgets/matrix.dart';
class TokenEmojiButton extends StatefulWidget {
final PangeaToken token;
final PangeaToken? token;
final String eventId;
final String targetId;
final VoidCallback onSelect;
final String? targetId;
final VoidCallback? onSelect;
const TokenEmojiButton({
super.key,
required this.token,
required this.eventId,
required this.targetId,
required this.onSelect,
this.targetId,
this.onSelect,
});
@override
@ -53,36 +53,45 @@ class TokenEmojiButtonState extends State<TokenEmojiButton>
@override
Widget build(BuildContext context) {
final emoji = widget.token.vocabConstructID.userSetEmoji.firstOrNull;
final eligible = widget.token?.lemma.saveVocab ?? false;
final emoji = widget.token?.vocabConstructID.userSetEmoji.firstOrNull;
if (_sizeAnimation != null) {
return CompositedTransformTarget(
link: MatrixState.pAnyState.layerLinkAndKey(widget.targetId).link,
child: AnimatedBuilder(
key: MatrixState.pAnyState.layerLinkAndKey(widget.targetId).key,
animation: _sizeAnimation!,
builder: (context, child) {
return Container(
height: _sizeAnimation!.value,
width: _sizeAnimation!.value,
alignment: Alignment.center,
child: InkWell(
onTap: widget.onSelect,
borderRadius: BorderRadius.circular(99.0),
child: emoji != null
? Text(
emoji,
style: TextStyle(fontSize: buttonSize - 4.0),
)
: Icon(
Icons.add_reaction_outlined,
size: buttonSize - 4.0,
color: Theme.of(context).colorScheme.primary,
),
),
);
},
),
final content = AnimatedBuilder(
key: widget.targetId != null
? MatrixState.pAnyState.layerLinkAndKey(widget.targetId!).key
: null,
animation: _sizeAnimation!,
builder: (context, child) {
return Container(
height: _sizeAnimation!.value,
width: eligible ? _sizeAnimation!.value : 0,
alignment: Alignment.center,
child: eligible
? InkWell(
onTap: widget.onSelect,
borderRadius: BorderRadius.circular(99.0),
child: emoji != null
? Text(
emoji,
style: TextStyle(fontSize: buttonSize - 4.0),
)
: Icon(
Icons.add_reaction_outlined,
size: buttonSize - 4.0,
color: Theme.of(context).colorScheme.primary,
),
)
: null,
);
},
);
return widget.targetId != null
? CompositedTransformTarget(
link:
MatrixState.pAnyState.layerLinkAndKey(widget.targetId!).link,
child: content,
)
: content;
}
return const SizedBox.shrink();