mirror of
https://github.com/nostrlabs-io/zap-stream-flutter.git
synced 2025-06-16 11:58:50 +00:00
@ -1,24 +1,45 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:ndk/ndk.dart';
|
||||
import 'package:ndk/shared/nips/nip19/nip19.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:zap_stream_flutter/imgproxy.dart';
|
||||
import 'package:zap_stream_flutter/theme.dart';
|
||||
import 'package:zap_stream_flutter/utils.dart';
|
||||
import 'package:zap_stream_flutter/widgets/custom_emoji.dart';
|
||||
import 'package:zap_stream_flutter/widgets/note_embed.dart';
|
||||
import 'package:zap_stream_flutter/widgets/profile.dart';
|
||||
import 'package:zap_stream_flutter/widgets/video_player.dart';
|
||||
|
||||
class NoteText extends StatelessWidget {
|
||||
final Nip01Event event;
|
||||
final bool? embedMedia;
|
||||
final bool? showEmbeds;
|
||||
|
||||
const NoteText({super.key, required this.event});
|
||||
const NoteText({
|
||||
super.key,
|
||||
required this.event,
|
||||
this.embedMedia,
|
||||
this.showEmbeds,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// use markdown rendering for articles
|
||||
if (event.kind == 30_023) {
|
||||
return MarkdownBody(data: event.content);
|
||||
}
|
||||
return RichText(
|
||||
text: TextSpan(
|
||||
children: textToSpans(event.content, event.tags, event.pubKey),
|
||||
children: textToSpans(
|
||||
event.content,
|
||||
event.tags,
|
||||
event.pubKey,
|
||||
showEmbeds: showEmbeds,
|
||||
embedMedia: embedMedia,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -26,21 +47,18 @@ class NoteText extends StatelessWidget {
|
||||
|
||||
/// Converts a nostr note text containing links
|
||||
/// and mentions into multiple spans for rendering
|
||||
/// /// https://github.com/leo-lox/camelus/blob/f58455a0ac07fcc780bdc69b8f4544fd5ea4a46d/lib/presentation_layer/components/note_card/note_card_build_split_content.dart#L262
|
||||
List<InlineSpan> textToSpans(
|
||||
String content,
|
||||
List<List<String>> tags,
|
||||
String pubkey,
|
||||
) {
|
||||
return _buildContentSpans(content.trim(), tags);
|
||||
}
|
||||
|
||||
/// Content parser from camelus
|
||||
/// https://github.com/leo-lox/camelus/blob/f58455a0ac07fcc780bdc69b8f4544fd5ea4a46d/lib/presentation_layer/components/note_card/note_card_build_split_content.dart#L262
|
||||
List<InlineSpan> _buildContentSpans(String content, List<List<String>> tags) {
|
||||
String pubkey, {
|
||||
bool? showEmbeds,
|
||||
bool? embedMedia,
|
||||
}) {
|
||||
List<InlineSpan> spans = [];
|
||||
RegExp exp = RegExp(
|
||||
r'nostr:(nprofile|npub)[a-zA-Z0-9]+|'
|
||||
r'nostr:(note|nevent)[a-zA-Z0-9]+|'
|
||||
r'nostr:(note|nevent|naddr)[a-zA-Z0-9]+|'
|
||||
r'(#\$\$\s*[0-9]+\s*\$\$)|'
|
||||
r'(#\w+)|' // Hashtags
|
||||
r'(https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*))', // URLs
|
||||
@ -53,11 +71,11 @@ List<InlineSpan> _buildContentSpans(String content, List<List<String>> tags) {
|
||||
String? matched = match.group(0);
|
||||
if (matched != null) {
|
||||
if (matched.startsWith('nostr:')) {
|
||||
spans.add(_buildProfileOrNoteSpan(matched));
|
||||
spans.add(_buildProfileOrNoteSpan(matched, showEmbeds ?? true));
|
||||
} else if (matched.startsWith('#')) {
|
||||
spans.add(_buildHashtagSpan(matched));
|
||||
} else if (matched.startsWith('http')) {
|
||||
spans.add(_buildUrlSpan(matched));
|
||||
spans.add(_buildUrlSpan(matched, embedMedia ?? false));
|
||||
}
|
||||
}
|
||||
return '';
|
||||
@ -87,12 +105,14 @@ List<InlineSpan> _buildContentSpans(String content, List<List<String>> tags) {
|
||||
return spans;
|
||||
}
|
||||
|
||||
InlineSpan _buildProfileOrNoteSpan(String word) {
|
||||
InlineSpan _buildProfileOrNoteSpan(String word, bool showEmbeds) {
|
||||
final cleanedWord = word.replaceAll('nostr:', '');
|
||||
final isProfile =
|
||||
cleanedWord.startsWith('nprofile') || cleanedWord.startsWith('npub');
|
||||
final isNote =
|
||||
cleanedWord.startsWith('note') || cleanedWord.startsWith('nevent');
|
||||
cleanedWord.startsWith('note') ||
|
||||
cleanedWord.startsWith('nevent') ||
|
||||
cleanedWord.startsWith("naddr");
|
||||
|
||||
if (isProfile) {
|
||||
final hexKey = bech32ToHex(cleanedWord);
|
||||
@ -102,11 +122,13 @@ InlineSpan _buildProfileOrNoteSpan(String word) {
|
||||
return TextSpan(text: "@$cleanedWord");
|
||||
}
|
||||
}
|
||||
if (isNote) {
|
||||
final eventId = bech32ToHex(cleanedWord);
|
||||
return TextSpan(text: eventId, style: TextStyle(color: PRIMARY_1));
|
||||
if (isNote && showEmbeds) {
|
||||
return WidgetSpan(
|
||||
child: NoteEmbedWidget(link: cleanedWord),
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
);
|
||||
} else {
|
||||
return TextSpan(text: word);
|
||||
return TextSpan(text: word, style: TextStyle(color: PRIMARY_1));
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,7 +136,29 @@ InlineSpan _buildHashtagSpan(String word) {
|
||||
return TextSpan(text: word, style: TextStyle(color: PRIMARY_1));
|
||||
}
|
||||
|
||||
InlineSpan _buildUrlSpan(String url) {
|
||||
InlineSpan _buildUrlSpan(String url, bool embedMedia) {
|
||||
if (embedMedia &&
|
||||
(url.endsWith(".jpg") ||
|
||||
url.endsWith(".gif") ||
|
||||
url.endsWith(".jpeg") ||
|
||||
url.endsWith(".webp") ||
|
||||
url.endsWith(".png") ||
|
||||
url.endsWith(".bmp"))) {
|
||||
return WidgetSpan(child: ProxyImg(url: url));
|
||||
}
|
||||
if (embedMedia &&
|
||||
(url.endsWith(".mp4") ||
|
||||
url.endsWith(".mov") ||
|
||||
url.endsWith(".webm") ||
|
||||
url.endsWith(".mkv") ||
|
||||
url.endsWith(".m3u8"))) {
|
||||
return WidgetSpan(
|
||||
child: AspectRatio(
|
||||
aspectRatio: 16 / 9,
|
||||
child: Center(child: VideoPlayerWidget(url: url, autoPlay: false)),
|
||||
),
|
||||
);
|
||||
}
|
||||
return TextSpan(
|
||||
text: url,
|
||||
style: TextStyle(color: PRIMARY_1),
|
||||
|
Reference in New Issue
Block a user