From 86a8181aea87c5bd2b97a7a92c34f0652a8a0c99 Mon Sep 17 00:00:00 2001 From: Kieran Date: Mon, 19 May 2025 11:44:13 +0100 Subject: [PATCH] feat: chat badges closes #15 --- lib/widgets/chat.dart | 19 +++++++++++++++++++ lib/widgets/chat_badge.dart | 25 +++++++++++++++++++++++++ lib/widgets/chat_message.dart | 18 +++++++++++++++++- 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/lib/widgets/chat.dart b/lib/widgets/chat.dart index 00558de..41c69e3 100644 --- a/lib/widgets/chat.dart +++ b/lib/widgets/chat.dart @@ -64,6 +64,16 @@ class ChatWidget extends StatelessWidget { .where((e) => e.kind == 9735) .map((e) => ZapReceipt.fromEvent(e)) .toList(); + // pubkey -> Set + final badgeAwards = filteredChat + .where((e) => e.kind == 8) + .map((e) => e.getTags("p").map((p) => (p, e.getFirstTag("a")!))) + .expand((v) => v) + .groupFoldBy( + (e) => e.$1, + (Set? acc, v) => (acc ?? {})..add(v.$2), + ); + return Column( spacing: 8, crossAxisAlignment: CrossAxisAlignment.start, @@ -81,6 +91,15 @@ class ChatWidget extends StatelessWidget { key: Key("chat:${filteredChat[idx].id}"), stream: stream, msg: filteredChat[idx], + badges: + badgeAwards[filteredChat[idx].pubKey] + ?.map( + (a) => ChatBadgeWidget.fromATag( + a, + key: Key("${filteredChat[idx].pubKey}:$a"), + ), + ) + .toList(), ), 1312 => ChatRaidMessage( event: filteredChat[idx], diff --git a/lib/widgets/chat_badge.dart b/lib/widgets/chat_badge.dart index ee8be8e..52ae7d8 100644 --- a/lib/widgets/chat_badge.dart +++ b/lib/widgets/chat_badge.dart @@ -72,3 +72,28 @@ class ChatBadgeAwardWidget extends StatelessWidget { ); } } + +class ChatBadgeWidget extends StatelessWidget { + final Nip01Event badge; + + const ChatBadgeWidget({super.key, required this.badge}); + + static Widget fromATag(String aTag, {Key? key}) { + return FutureBuilder( + future: ndk.requests.query(filters: [aTagToFilter(aTag)]).future, + builder: (context, state) { + final ev = state.data?.firstOrNull; + if (ev == null) return SizedBox(); + return ChatBadgeWidget(badge: ev, key: key); + }, + ); + } + + @override + Widget build(BuildContext context) { + final image = badge.getFirstTag("image"); + if (image?.isEmpty ?? true) return SizedBox(); + + return ProxyImg(url: image, resize: 24, height: 24, key: UniqueKey()); + } +} diff --git a/lib/widgets/chat_message.dart b/lib/widgets/chat_message.dart index bdf6bfa..84150ce 100644 --- a/lib/widgets/chat_message.dart +++ b/lib/widgets/chat_message.dart @@ -13,8 +13,14 @@ import 'package:zap_stream_flutter/widgets/profile.dart'; class ChatMessageWidget extends StatelessWidget { final StreamEvent stream; final Nip01Event msg; + final List? badges; - const ChatMessageWidget({super.key, required this.stream, required this.msg}); + const ChatMessageWidget({ + super.key, + required this.stream, + required this.msg, + this.badges, + }); @override Widget build(BuildContext context) { @@ -60,6 +66,16 @@ class ChatMessageWidget extends StatelessWidget { ), ), ), + if (badges?.isNotEmpty ?? false) TextSpan(text: " "), + if (badges?.isNotEmpty ?? false) + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Row( + spacing: 4, + mainAxisSize: MainAxisSize.min, + children: badges!, + ), + ), TextSpan(text: " "), ...textToSpans(msg.content, msg.tags, msg.pubKey), ],