diff --git a/lib/widgets/chat_message.dart b/lib/widgets/chat_message.dart index 56c8e90..1f72a22 100644 --- a/lib/widgets/chat_message.dart +++ b/lib/widgets/chat_message.dart @@ -42,22 +42,7 @@ class ChatMessageWidget extends StatelessWidget { }, child: Padding( padding: EdgeInsets.symmetric(horizontal: 2, vertical: 4), - child: Column( - spacing: 2, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _chatText(context, profile), - /*RxFilter( - Key(msg.id), - filters: [ - Filter(kinds: [9735, 7], eTags: [msg.id]), - ], - builder: (context, state) { - return ChatReactions(msg: msg, events: state ?? []); - }, - ),*/ - ], - ), + child: _chatText(context, profile), ), ); }); @@ -104,64 +89,3 @@ class ChatMessageWidget extends StatelessWidget { ); } } - -class ChatReactions extends StatelessWidget { - final Nip01Event msg; - final List events; - - const ChatReactions({super.key, required this.msg, required this.events}); - - @override - Widget build(BuildContext context) { - // reactions must have e tag pointing to msg - final filteredEvents = events.where((e) => e.getEId() == msg.id); - if (filteredEvents.isEmpty) return SizedBox.shrink(); - final zaps = filteredEvents - .where((e) => e.kind == 9735) - .map((e) => ZapReceipt.fromEvent(e)); - final reactions = filteredEvents.where((e) => e.kind == 7); - - return Row( - spacing: 8, - children: [ - if (zaps.isNotEmpty) - Container( - padding: EdgeInsets.symmetric(horizontal: 4, vertical: 2), - decoration: BoxDecoration(color: LAYER_2, borderRadius: DEFAULT_BR), - child: Row( - children: [ - Icon(Icons.bolt, color: ZAP_1, size: 16), - Text( - formatSats( - zaps.fold(0, (acc, v) => acc + (v.amountSats ?? 0)), - ), - ), - ], - ), - ), - if (reactions.isNotEmpty) - ...reactions - .fold(>{}, (acc, v) { - // ignore: prefer_collection_literals - acc[v.content] ??= Set(); - acc[v.content]!.add(v); - return acc; - }) - .entries - .map( - (v) => Container( - padding: EdgeInsets.symmetric(horizontal: 4, vertical: 2), - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - color: LAYER_2, - borderRadius: DEFAULT_BR, - ), - child: Center( - child: CustomEmoji(emoji: v.key, tags: v.value.first.tags), - ), - ), - ), - ], - ); - } -} diff --git a/lib/widgets/chat_modal.dart b/lib/widgets/chat_modal.dart index 91d33b6..fd5028d 100644 --- a/lib/widgets/chat_modal.dart +++ b/lib/widgets/chat_modal.dart @@ -6,6 +6,7 @@ import 'package:zap_stream_flutter/const.dart'; import 'package:zap_stream_flutter/theme.dart'; import 'package:zap_stream_flutter/utils.dart'; import 'package:zap_stream_flutter/widgets/button_follow.dart'; +import 'package:zap_stream_flutter/widgets/chat_reactions.dart'; import 'package:zap_stream_flutter/widgets/mute_button.dart'; import 'package:zap_stream_flutter/widgets/nostr_text.dart'; import 'package:zap_stream_flutter/widgets/pill.dart'; @@ -50,6 +51,7 @@ class _ChatModalWidget extends State { padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), child: NoteText(event: widget.event, showEmbeds: false), ), + ChatReactions.forMessage(widget.event), Row( spacing: 8, children: [ @@ -107,7 +109,11 @@ class _ChatModalWidget extends State { ), ], ), - if (_showEmojiPicker) ReactionWidget(event: widget.event), + if (_showEmojiPicker) + ReactionWidget( + event: widget.event, + customEmojiSets: [widget.stream.info.host], + ), if (_showTimeoutOptions) GridView.count( diff --git a/lib/widgets/chat_reactions.dart b/lib/widgets/chat_reactions.dart new file mode 100644 index 0000000..70b268c --- /dev/null +++ b/lib/widgets/chat_reactions.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import 'package:ndk/ndk.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/custom_emoji.dart'; + +class ChatReactions extends StatelessWidget { + final Nip01Event msg; + final List events; + + const ChatReactions({super.key, required this.msg, required this.events}); + + static Widget forMessage(Nip01Event ev) { + return RxFilter( + Key("chat:reactions:${ev.id}"), + filters: [ + Filter(kinds: [7, 9735], eTags: [ev.id]), + ], + builder: (context, state) { + return ChatReactions(msg: ev, events: state ?? []); + }, + ); + } + + @override + Widget build(BuildContext context) { + // reactions must have e tag pointing to msg + final filteredEvents = events.where((e) => e.getEId() == msg.id); + if (filteredEvents.isEmpty) return SizedBox.shrink(); + final zaps = filteredEvents + .where((e) => e.kind == 9735) + .map((e) => ZapReceipt.fromEvent(e)); + final reactions = filteredEvents.where((e) => e.kind == 7); + + return Row( + spacing: 8, + children: [ + if (zaps.isNotEmpty) + Container( + padding: EdgeInsets.symmetric(horizontal: 4, vertical: 2), + decoration: BoxDecoration(color: LAYER_2, borderRadius: DEFAULT_BR), + child: Row( + children: [ + Icon(Icons.bolt, color: ZAP_1, size: 16), + Text( + formatSats( + zaps.fold(0, (acc, v) => acc + (v.amountSats ?? 0)), + ), + ), + ], + ), + ), + if (reactions.isNotEmpty) + ...reactions + .fold(>{}, (acc, v) { + // ignore: prefer_collection_literals + acc[v.content] ??= Set(); + acc[v.content]!.add(v); + return acc; + }) + .entries + .map( + (v) => Container( + padding: EdgeInsets.symmetric(horizontal: 4, vertical: 2), + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: LAYER_2, + borderRadius: DEFAULT_BR, + ), + child: Center( + child: CustomEmoji(emoji: v.key, tags: v.value.first.tags), + ), + ), + ), + ], + ); + } +} diff --git a/lib/widgets/reaction.dart b/lib/widgets/reaction.dart index 5aa7339..53943b9 100644 --- a/lib/widgets/reaction.dart +++ b/lib/widgets/reaction.dart @@ -6,16 +6,30 @@ import 'package:zap_stream_flutter/widgets/emoji.dart'; class ReactionWidget extends StatelessWidget { final Nip01Event event; + final List? customEmojiSets; - const ReactionWidget({super.key, required this.event}); + const ReactionWidget({super.key, required this.event, this.customEmojiSets}); @override Widget build(BuildContext context) { return EmojiPickerCustom( onEmojiSelected: (e) { - ndk.broadcast.broadcastReaction(eventId: event.id, reaction: e.emoji); + final signer = ndk.accounts.getLoggedAccount()?.signer; + if (signer == null) return; + final ev = Nip01Event( + pubKey: signer.getPublicKey(), + kind: 7, + tags: [ + ["e", event.id], + ["p", event.pubKey], + if (e.emoji.startsWith("http")) ["emoji", e.name, e.emoji], + ], + content: e.emoji.startsWith("http") ? ":${e.name}:" : e.emoji, + ); + ndk.broadcast.broadcast(nostrEvent: ev); context.pop(); }, + customEmojiSets: customEmojiSets, ); } }