feat: top zappers

closes #8
This commit is contained in:
2025-05-12 11:18:05 +01:00
parent 658bddbef0
commit 194bff315c
3 changed files with 111 additions and 41 deletions

View File

@ -139,7 +139,6 @@ class _StreamPage extends State<StreamPage> {
), ),
], ],
), ),
SizedBox(height: 10),
Expanded(child: ChatWidget(stream: widget.stream)), Expanded(child: ChatWidget(stream: widget.stream)),
], ],
); );

View File

@ -17,18 +17,20 @@ class ChatWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return RxFilter<Nip01Event>(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: SingleChildScrollView(
reverse: true,
child: RxFilter<Nip01Event>(
filters: [ filters: [
Filter(kinds: [1311, 9735], limit: 200, aTags: [stream.aTag]), Filter(kinds: [1311, 9735], limit: 200, aTags: [stream.aTag]),
], ],
builder: (ctx, state) { builder: (ctx, state) {
return Column( return Column(
spacing: 8,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_TopZappersWidget(events: state ?? []),
Expanded(
child: SingleChildScrollView(
reverse: true,
child: Column(
spacing: 8, spacing: 8,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: children:
@ -37,13 +39,11 @@ class ChatWidget extends StatelessWidget {
.map( .map(
(c) => switch (c.kind) { (c) => switch (c.kind) {
1311 => ChatMessageWidget(stream: stream, msg: c), 1311 => ChatMessageWidget(stream: stream, msg: c),
9735 => ChatZapWidget(stream: stream, zap: c), 9735 => _ChatZapWidget(stream: stream, zap: c),
_ => SizedBox.shrink(), _ => SizedBox.shrink(),
}, },
) )
.toList(), .toList(),
);
},
), ),
), ),
), ),
@ -55,10 +55,7 @@ class ChatWidget extends StatelessWidget {
margin: EdgeInsets.symmetric(vertical: 8), margin: EdgeInsets.symmetric(vertical: 8),
width: double.maxFinite, width: double.maxFinite,
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(borderRadius: DEFAULT_BR),
color: PRIMARY_1,
borderRadius: DEFAULT_BR,
),
child: Text( child: Text(
"STREAM ENDED", "STREAM ENDED",
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),
@ -66,14 +63,71 @@ class ChatWidget extends StatelessWidget {
), ),
], ],
); );
},
);
} }
} }
class ChatZapWidget extends StatelessWidget { class _TopZappersWidget extends StatelessWidget {
final List<Nip01Event> events;
const _TopZappersWidget({required this.events});
@override
Widget build(BuildContext context) {
final topZaps =
events
.where((e) => e.kind == 9735)
.map((e) => ZapReceipt.fromEvent(e))
.fold(<String, int>{}, (acc, e) {
if (e.sender != null) {
acc[e.sender!] = (acc[e.sender!] ?? 0) + e.amountSats!;
}
return acc;
})
.entries
.sortedBy((e) => e.value)
.reversed
.take(10)
.toList();
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
primary: false,
child: Row(
spacing: 10,
children:
topZaps
.map(
(v) => Container(
padding: EdgeInsets.only(left: 4, right: 8),
decoration: BoxDecoration(
borderRadius: DEFAULT_BR,
border: Border.all(color: LAYER_3),
),
child: ProfileWidget.pubkey(
v.key,
showName: false,
size: 20,
spacing: 0,
children: [
Icon(Icons.bolt, color: ZAP_1),
Text(formatSats(v.value)),
],
),
),
)
.toList(),
),
);
}
}
class _ChatZapWidget extends StatelessWidget {
final StreamEvent stream; final StreamEvent stream;
final Nip01Event zap; final Nip01Event zap;
const ChatZapWidget({super.key, required this.stream, required this.zap}); const _ChatZapWidget({required this.stream, required this.zap});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -125,6 +179,7 @@ class ChatZapWidget extends StatelessWidget {
padding: EdgeInsets.only(right: 8), padding: EdgeInsets.only(right: 8),
child: AvatarWidget(profile: profile, size: 20), child: AvatarWidget(profile: profile, size: 20),
), ),
alignment: PlaceholderAlignment.middle,
), ),
TextSpan(text: name), TextSpan(text: name),
TextSpan(text: " zapped ", style: TextStyle(color: FONT_COLOR)), TextSpan(text: " zapped ", style: TextStyle(color: FONT_COLOR)),

View File

@ -64,19 +64,34 @@ class ProfileWidget extends StatelessWidget {
final Metadata profile; final Metadata profile;
final TextStyle? style; final TextStyle? style;
final double? size; final double? size;
final List<Widget>? children;
final bool? showName;
final double? spacing;
const ProfileWidget({ const ProfileWidget({
super.key, super.key,
required this.profile, required this.profile,
this.style, this.style,
this.size, this.size,
this.children,
this.showName,
this.spacing,
}); });
static Widget pubkey(String pubkey, {double? size}) { static Widget pubkey(
String pubkey, {
double? size,
List<Widget>? children,
bool? showName,
double? spacing,
}) {
return ProfileLoaderWidget(pubkey, (ctx, state) { return ProfileLoaderWidget(pubkey, (ctx, state) {
return ProfileWidget( return ProfileWidget(
profile: state.data ?? Metadata(pubKey: pubkey), profile: state.data ?? Metadata(pubKey: pubkey),
size: size, size: size,
showName: showName,
spacing: spacing,
children: children,
); );
}); });
} }
@ -84,10 +99,11 @@ class ProfileWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
spacing: 8, spacing: spacing ?? 8,
children: [ children: [
AvatarWidget(profile: profile, size: size), AvatarWidget(profile: profile, size: size),
ProfileNameWidget(profile: profile), if (showName ?? true) ProfileNameWidget(profile: profile),
...(children ?? []),
], ],
); );
} }