mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix(mobile): open profile sheet when tapping @mentions (#1876)
Signed-off-by: npub1qq582hwclq7jnux44a2xul8nhe2ue2zk9z49ehzngznnmmk5ka5sprvchv <0028755dd8f83d29f0d5af546e7cf3be55cca85628aa5cdc5340a73deed4b769@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub1qq582hwclq7jnux44a2xul8nhe2ue2zk9z49ehzngznnmmk5ka5sprvchv <0028755dd8f83d29f0d5af546e7cf3be55cca85628aa5cdc5340a73deed4b769@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
co-authored by
npub1qq582hwclq7jnux44a2xul8nhe2ue2zk9z49ehzngznnmmk5ka5sprvchv
parent
59e9821503
commit
7e009a8937
@@ -122,6 +122,8 @@ class _MessageBubble extends ConsumerWidget {
|
||||
currentChannelId: currentChannelId,
|
||||
);
|
||||
},
|
||||
onMentionTap: (pubkey) =>
|
||||
showUserProfileSheet(context, pubkey),
|
||||
),
|
||||
if (message.reactions.isNotEmpty)
|
||||
ReactionRow(
|
||||
|
||||
@@ -39,6 +39,10 @@ class MessageContent extends HookConsumerWidget {
|
||||
/// Called when a #channel link is tapped.
|
||||
final void Function(String channelId)? onChannelTap;
|
||||
|
||||
/// Called when an @mention of a known user is tapped, with the
|
||||
/// mentioned user's pubkey.
|
||||
final void Function(String pubkey)? onMentionTap;
|
||||
|
||||
final TextStyle? baseStyle;
|
||||
|
||||
final int? maxLines;
|
||||
@@ -50,6 +54,7 @@ class MessageContent extends HookConsumerWidget {
|
||||
this.channelNames = const {},
|
||||
this.tags = const [],
|
||||
this.onChannelTap,
|
||||
this.onMentionTap,
|
||||
this.baseStyle,
|
||||
this.maxLines,
|
||||
});
|
||||
@@ -145,7 +150,7 @@ class MessageContent extends HookConsumerWidget {
|
||||
_buildMedia(context, imageUrl, imetaByUrl[imageUrl]),
|
||||
maxLines: maxLines,
|
||||
inlineComponents: [
|
||||
_MentionMd(mentionNames: mentionNames),
|
||||
_MentionMd(mentionNames: mentionNames, onMentionTap: onMentionTap),
|
||||
CustomEmojiMd(customEmoji),
|
||||
_ChannelLinkMd(channelNames: channelNames, onChannelTap: onChannelTap),
|
||||
...MarkdownComponent.inlineComponents,
|
||||
@@ -572,13 +577,14 @@ class _MessageCodeBlock extends HookWidget {
|
||||
|
||||
class _MentionMd extends InlineMd {
|
||||
final Map<String, String> mentionNames;
|
||||
final void Function(String pubkey)? onMentionTap;
|
||||
late final RegExp _exp = _buildPrefixPattern(
|
||||
prefix: '@',
|
||||
knownNames: _mentionAliases(mentionNames.values),
|
||||
genericTokenPattern: r'[A-Za-z0-9_][A-Za-z0-9_\u00A0-]*',
|
||||
);
|
||||
|
||||
_MentionMd({required this.mentionNames});
|
||||
_MentionMd({required this.mentionNames, this.onMentionTap});
|
||||
|
||||
@override
|
||||
RegExp get exp => _exp;
|
||||
@@ -596,22 +602,28 @@ class _MentionMd extends InlineMd {
|
||||
|
||||
final name = raw.substring(1).replaceAll('\u00A0', ' ').toLowerCase();
|
||||
String? displayName;
|
||||
String? pubkey;
|
||||
for (final entry in mentionNames.entries) {
|
||||
final entryName = entry.value.toLowerCase();
|
||||
final firstName = entryName.split(RegExp(r'\s+')).first;
|
||||
if (entryName == name || firstName == name) {
|
||||
displayName = entry.value;
|
||||
pubkey = entry.key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final pill = _TokenPill(
|
||||
text: '@${displayName ?? raw.substring(1)}',
|
||||
textStyle: config.style,
|
||||
);
|
||||
|
||||
return WidgetSpan(
|
||||
alignment: PlaceholderAlignment.baseline,
|
||||
baseline: TextBaseline.alphabetic,
|
||||
child: _TokenPill(
|
||||
text: '@${displayName ?? raw.substring(1)}',
|
||||
textStyle: config.style,
|
||||
),
|
||||
child: pubkey != null && onMentionTap != null
|
||||
? GestureDetector(onTap: () => onMentionTap!(pubkey!), child: pill)
|
||||
: pill,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,6 +505,8 @@ class _ThreadMessage extends ConsumerWidget {
|
||||
currentChannelId: channelId,
|
||||
);
|
||||
},
|
||||
onMentionTap: (pubkey) =>
|
||||
showUserProfileSheet(context, pubkey),
|
||||
),
|
||||
if (message.reactions.isNotEmpty)
|
||||
ReactionRow(
|
||||
|
||||
@@ -363,6 +363,7 @@ class _OriginalPost extends ConsumerWidget {
|
||||
content: post.content,
|
||||
mentionNames: mentionNames,
|
||||
tags: post.tags,
|
||||
onMentionTap: (pubkey) => showUserProfileSheet(context, pubkey),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -457,6 +458,7 @@ class _ReplyRow extends ConsumerWidget {
|
||||
content: reply.content,
|
||||
mentionNames: mentionNames,
|
||||
tags: reply.tags,
|
||||
onMentionTap: (pubkey) => showUserProfileSheet(context, pubkey),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -775,6 +775,56 @@ void main() {
|
||||
expect(_allRichText(tester), contains('alice@example.com'));
|
||||
expect(find.text('@example.com'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('mention tap callback fires with pubkey', (tester) async {
|
||||
String? tappedPubkey;
|
||||
await tester.pumpWidget(
|
||||
_testable(
|
||||
MessageContent(
|
||||
content: 'Hey @Alice check this out',
|
||||
mentionNames: const {'pk1': 'Alice'},
|
||||
onMentionTap: (pubkey) => tappedPubkey = pubkey,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('@Alice'));
|
||||
expect(tappedPubkey, 'pk1');
|
||||
});
|
||||
|
||||
testWidgets('multi-word mention tap callback fires with pubkey', (
|
||||
tester,
|
||||
) async {
|
||||
String? tappedPubkey;
|
||||
await tester.pumpWidget(
|
||||
_testable(
|
||||
MessageContent(
|
||||
content: 'Hey @Kenny Lopez can you review this?',
|
||||
mentionNames: const {'pk1': 'Kenny Lopez'},
|
||||
onMentionTap: (pubkey) => tappedPubkey = pubkey,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('@Kenny Lopez'));
|
||||
expect(tappedPubkey, 'pk1');
|
||||
});
|
||||
|
||||
testWidgets('unknown mention renders without tap', (tester) async {
|
||||
var tapped = false;
|
||||
await tester.pumpWidget(
|
||||
_testable(
|
||||
MessageContent(
|
||||
content: 'Hey @unknown check this',
|
||||
mentionNames: const {},
|
||||
onMentionTap: (_) => tapped = true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('@unknown'), warnIfMissed: false);
|
||||
expect(tapped, isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('#channel links', () {
|
||||
|
||||
Reference in New Issue
Block a user