mirror of
https://github.com/nostrlabs-io/zap-stream-flutter.git
synced 2025-06-14 11:27:43 +00:00
refactor: move reactions to chat modal
fix: custom emoji reactions
This commit is contained in:
@ -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<Nip01Event>(
|
||||
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<Nip01Event> 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(<String, Set<Nip01Event>>{}, (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),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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<ChatModalWidget> {
|
||||
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<ChatModalWidget> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_showEmojiPicker) ReactionWidget(event: widget.event),
|
||||
if (_showEmojiPicker)
|
||||
ReactionWidget(
|
||||
event: widget.event,
|
||||
customEmojiSets: [widget.stream.info.host],
|
||||
),
|
||||
|
||||
if (_showTimeoutOptions)
|
||||
GridView.count(
|
||||
|
79
lib/widgets/chat_reactions.dart
Normal file
79
lib/widgets/chat_reactions.dart
Normal file
@ -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<Nip01Event> events;
|
||||
|
||||
const ChatReactions({super.key, required this.msg, required this.events});
|
||||
|
||||
static Widget forMessage(Nip01Event ev) {
|
||||
return RxFilter<Nip01Event>(
|
||||
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(<String, Set<Nip01Event>>{}, (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),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -6,16 +6,30 @@ import 'package:zap_stream_flutter/widgets/emoji.dart';
|
||||
|
||||
class ReactionWidget extends StatelessWidget {
|
||||
final Nip01Event event;
|
||||
final List<String>? 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user