From 46d60994a83e277bf8b4fe6e6a303f2aa5202c46 Mon Sep 17 00:00:00 2001 From: Kieran Date: Mon, 19 May 2025 11:11:12 +0100 Subject: [PATCH] feat: badge awards --- lib/utils.dart | 5 +++ lib/widgets/chat.dart | 6 +++ lib/widgets/chat_badge.dart | 74 +++++++++++++++++++++++++++++++++++ lib/widgets/chat_raid.dart | 13 ++---- lib/widgets/stream_cards.dart | 3 -- 5 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 lib/widgets/chat_badge.dart diff --git a/lib/utils.dart b/lib/utils.dart index 9cbcf23..c660760 100644 --- a/lib/utils.dart +++ b/lib/utils.dart @@ -580,3 +580,8 @@ TLVEntity decodeBech32ToTLVEntity(String input) { return TLVEntity(data.hrp, [TLV(0, data8bit.length, data8bit)]); } } + +Filter aTagToFilter(String tag) { + final ts = tag.split(":"); + return Filter(kinds: [int.parse(ts[0])], authors: [ts[1]], dTags: [ts[2]]); +} diff --git a/lib/widgets/chat.dart b/lib/widgets/chat.dart index d908914..00558de 100644 --- a/lib/widgets/chat.dart +++ b/lib/widgets/chat.dart @@ -5,6 +5,7 @@ import 'package:zap_stream_flutter/main.dart'; import 'package:zap_stream_flutter/rx_filter.dart'; import 'package:zap_stream_flutter/theme.dart'; import 'package:zap_stream_flutter/utils.dart'; +import 'package:zap_stream_flutter/widgets/chat_badge.dart'; import 'package:zap_stream_flutter/widgets/chat_message.dart'; import 'package:zap_stream_flutter/widgets/chat_raid.dart'; import 'package:zap_stream_flutter/widgets/chat_write.dart'; @@ -27,6 +28,7 @@ class ChatWidget extends StatelessWidget { Filter(kinds: [1311, 9735], limit: 200, aTags: [stream.aTag]), Filter(kinds: [1312], limit: 200, aTags: [stream.aTag]), Filter(kinds: [Nip51List.kMute], authors: muteLists), + Filter(kinds: [8], authors: [stream.info.host]), ]; return RxFilter( Key("stream:chat:${stream.aTag}"), @@ -89,6 +91,10 @@ class ChatWidget extends StatelessWidget { stream: stream, zap: filteredChat[idx], ), + 8 => ChatBadgeAwardWidget( + event: filteredChat[idx], + stream: stream, + ), _ => SizedBox(), }, ), diff --git a/lib/widgets/chat_badge.dart b/lib/widgets/chat_badge.dart new file mode 100644 index 0000000..ee8be8e --- /dev/null +++ b/lib/widgets/chat_badge.dart @@ -0,0 +1,74 @@ +import 'package:flutter/widgets.dart'; +import 'package:ndk/entities.dart'; +import 'package:zap_stream_flutter/imgproxy.dart'; +import 'package:zap_stream_flutter/main.dart'; +import 'package:zap_stream_flutter/theme.dart'; +import 'package:zap_stream_flutter/utils.dart'; +import 'package:zap_stream_flutter/widgets/profile.dart'; + +class ChatBadgeAwardWidget extends StatelessWidget { + final Nip01Event event; + final StreamEvent stream; + + const ChatBadgeAwardWidget({ + super.key, + required this.event, + required this.stream, + }); + + @override + Widget build(BuildContext context) { + final aTag = event.getFirstTag("a"); + if (aTag == null) return SizedBox(); + + return Container( + margin: EdgeInsets.symmetric(horizontal: 2, vertical: 4), + padding: EdgeInsets.all(5), + decoration: BoxDecoration(color: LAYER_1, borderRadius: DEFAULT_BR), + child: FutureBuilder( + future: ndk.requests.query(filters: [aTagToFilter(aTag)]).future, + builder: (context, state) { + final badge = state.data?.firstOrNull; + final image = badge?.getFirstTag("image"); + final name = badge?.getFirstTag("name"); + final title = badge?.getFirstTag("description"); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 8, + children: [ + Column( + spacing: 8, + children: [ + if (image?.isNotEmpty ?? false) + ProxyImg(url: image, width: 64), + if (name?.isNotEmpty ?? false) + Text( + name!, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20, + ), + ), + if (title?.isNotEmpty ?? false) + Text( + title!, + style: TextStyle(color: LAYER_5), + textAlign: TextAlign.center, + ), + ], + ), + Text( + "Awarded to: ", + style: TextStyle(fontWeight: FontWeight.w500), + ), + ...event + .getTags("p") + .map((e) => ProfileWidget.pubkey(e, size: 20)), + ], + ); + }, + ), + ); + } +} diff --git a/lib/widgets/chat_raid.dart b/lib/widgets/chat_raid.dart index 2efdd88..71db150 100644 --- a/lib/widgets/chat_raid.dart +++ b/lib/widgets/chat_raid.dart @@ -53,16 +53,9 @@ class __ChatRaidMessage extends State Widget build(BuildContext context) { if (_from == null || _to == null) return SizedBox.shrink(); - final otherLink = (_isRaiding ? _to : _from).split(":"); - final otherEvent = ndk.requests.query( - filters: [ - Filter( - kinds: [int.parse(otherLink[0])], - authors: [otherLink[1]], - dTags: [otherLink[2]], - ), - ], - ); + final otherTag = _isRaiding ? _to : _from; + final otherLink = otherTag.split(":"); + final otherEvent = ndk.requests.query(filters: [aTagToFilter(otherTag)]); return Container( padding: EdgeInsets.all(8), diff --git a/lib/widgets/stream_cards.dart b/lib/widgets/stream_cards.dart index b5fd468..ee5ea86 100644 --- a/lib/widgets/stream_cards.dart +++ b/lib/widgets/stream_cards.dart @@ -1,14 +1,11 @@ -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown_plus/flutter_markdown_plus.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:ndk/ndk.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:zap_stream_flutter/imgproxy.dart'; import 'package:zap_stream_flutter/rx_filter.dart'; import 'package:zap_stream_flutter/theme.dart'; import 'package:zap_stream_flutter/utils.dart'; -import 'package:zap_stream_flutter/widgets/nostr_text.dart'; class StreamCardsWidget extends StatelessWidget { final StreamEvent stream;