mirror of
https://github.com/nostrlabs-io/zap-stream-flutter.git
synced 2025-06-16 11:58:50 +00:00
feat: tag zap goals
This commit is contained in:
@ -120,6 +120,13 @@ class _StreamPage extends State<StreamPage> {
|
||||
return ZapWidget(
|
||||
pubkey: widget.stream.info.host,
|
||||
target: widget.stream.event,
|
||||
zapTags:
|
||||
// tag goal onto zap request
|
||||
widget.stream.info.goal != null
|
||||
? [
|
||||
["e", widget.stream.info.goal!],
|
||||
]
|
||||
: null,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -234,7 +234,7 @@ String formatSats(int n) {
|
||||
final fmt = NumberFormat();
|
||||
if (n >= 1000000) {
|
||||
return "${fmt.format(n / 1000000)}M";
|
||||
} else if (n >= 1000) {
|
||||
} else if (n >= 1500) {
|
||||
return "${fmt.format(n / 1000)}k";
|
||||
} else {
|
||||
return fmt.format(n);
|
||||
|
@ -136,23 +136,33 @@ class _StreamGoalWidget extends StatelessWidget {
|
||||
final max = int.parse(goal.getFirstTag("amount") ?? "1");
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 4),
|
||||
child: Column(
|
||||
spacing: 4,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(goal.content),
|
||||
RxFilter<Nip01Event>(
|
||||
filters: [
|
||||
Filter(kinds: [9735], eTags: [goal.id]),
|
||||
],
|
||||
builder: (ctx, state) {
|
||||
final zaps = (state ?? []).map((e) => ZapReceipt.fromEvent(e));
|
||||
final totalZaps =
|
||||
zaps.fold(0, (acc, v) => acc + (v.amountSats ?? 0)) * 1000;
|
||||
final progress = totalZaps / max;
|
||||
child: RxFilter<Nip01Event>(
|
||||
filters: [
|
||||
Filter(kinds: [9735], eTags: [goal.id]),
|
||||
],
|
||||
builder: (ctx, state) {
|
||||
final zaps = (state ?? []).map((e) => ZapReceipt.fromEvent(e));
|
||||
final totalZaps =
|
||||
zaps.fold(0, (acc, v) => acc + (v.amountSats ?? 0)) * 1000;
|
||||
final progress = totalZaps / max;
|
||||
final remaining = ((max - totalZaps).clamp(0, max) / 1000).toInt();
|
||||
|
||||
final q = MediaQuery.of(ctx);
|
||||
return Stack(
|
||||
final q = MediaQuery.of(ctx);
|
||||
return Column(
|
||||
spacing: 4,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: Text(goal.content)),
|
||||
if (remaining > 0)
|
||||
Text(
|
||||
"Remaining: ${formatSats(remaining)}",
|
||||
style: TextStyle(fontSize: 12, color: LAYER_5),
|
||||
),
|
||||
],
|
||||
),
|
||||
Stack(
|
||||
children: [
|
||||
Container(
|
||||
height: 10,
|
||||
@ -169,21 +179,33 @@ class _StreamGoalWidget extends StatelessWidget {
|
||||
borderRadius: DEFAULT_BR,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 2,
|
||||
child: Text(
|
||||
"Goal: ${formatSats((max / 1000).toInt())}",
|
||||
style: TextStyle(
|
||||
fontSize: 8,
|
||||
fontWeight: FontWeight.w500,
|
||||
if (remaining > 0)
|
||||
Positioned(
|
||||
right: 2,
|
||||
child: Text(
|
||||
"Goal: ${formatSats((max / 1000).toInt())}",
|
||||
style: TextStyle(
|
||||
fontSize: 8,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (remaining == 0)
|
||||
Center(
|
||||
child: Text(
|
||||
"COMPLETE",
|
||||
style: TextStyle(
|
||||
color: LAYER_0,
|
||||
fontSize: 8,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:clipboard/clipboard.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ndk/domain_layer/usecases/lnurl/lnurl.dart';
|
||||
import 'package:ndk/ndk.dart';
|
||||
@ -13,8 +14,16 @@ import 'package:zap_stream_flutter/widgets/profile.dart';
|
||||
class ZapWidget extends StatefulWidget {
|
||||
final String pubkey;
|
||||
final Nip01Event? target;
|
||||
final List<Nip01Event>? otherTargets;
|
||||
final List<List<String>>? zapTags;
|
||||
|
||||
const ZapWidget({super.key, required this.pubkey, this.target});
|
||||
const ZapWidget({
|
||||
super.key,
|
||||
required this.pubkey,
|
||||
this.target,
|
||||
this.zapTags,
|
||||
this.otherTargets,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _ZapWidget();
|
||||
@ -133,31 +142,53 @@ class _ZapWidget extends State<ZapWidget> {
|
||||
];
|
||||
}
|
||||
|
||||
Future<ZapRequest?> _makeZap() async {
|
||||
final signer = ndk.accounts.getLoggedAccount()?.signer;
|
||||
if (signer == null) return null;
|
||||
|
||||
var relays = defaultRelays;
|
||||
// if target event has relays tag, use that for zap
|
||||
if (widget.target?.tags.any((t) => t[0] == "relays") ?? false) {
|
||||
relays = widget.target!.tags.firstWhere((t) => t[0] == "relays").slice(1);
|
||||
}
|
||||
final amount = _amount! * 1000;
|
||||
|
||||
var tags = [
|
||||
["relays", ...relays],
|
||||
["amount", amount.toString()],
|
||||
["p", widget.pubkey],
|
||||
];
|
||||
|
||||
// tag targets for zap request
|
||||
for (final t in [
|
||||
...(widget.target != null ? [widget.target!] : []),
|
||||
...(widget.otherTargets != null ? widget.otherTargets! : []),
|
||||
]) {
|
||||
if (t.kind >= 30_000 && t.kind < 40_000) {
|
||||
tags.add(["a", "${t.kind}:${t.pubKey}:${t.getDtag()!}"]);
|
||||
} else {
|
||||
tags.add(["e", t.id]);
|
||||
}
|
||||
}
|
||||
if (widget.zapTags != null) {
|
||||
tags.addAll(widget.zapTags!);
|
||||
}
|
||||
var event = ZapRequest(
|
||||
pubKey: signer.getPublicKey(),
|
||||
tags: tags,
|
||||
content: _comment.text,
|
||||
);
|
||||
await signer.sign(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
Future<void> _loadZap() async {
|
||||
final profile = await ndk.metadata.loadMetadata(widget.pubkey);
|
||||
if (profile?.lud16 == null) {
|
||||
throw "No lightning address found";
|
||||
}
|
||||
final signer = ndk.accounts.getLoggedAccount()?.signer;
|
||||
|
||||
final zapRequest =
|
||||
signer != null
|
||||
? await ndk.zaps.createZapRequest(
|
||||
amountSats: _amount!,
|
||||
signer: signer,
|
||||
pubKey: widget.pubkey,
|
||||
eventId: widget.target?.id,
|
||||
addressableId:
|
||||
widget.target != null &&
|
||||
widget.target!.kind >= 30_000 &&
|
||||
widget.target!.kind < 40_000
|
||||
? "${widget.target!.kind}:${widget.target!.pubKey}:${widget.target!.getDtag()!}"
|
||||
: null,
|
||||
relays: defaultRelays,
|
||||
comment: _comment.text.isNotEmpty ? _comment.text : null,
|
||||
)
|
||||
: null;
|
||||
|
||||
final zapRequest = await _makeZap();
|
||||
final invoice = await ndk.zaps.fetchInvoice(
|
||||
lud16Link: Lnurl.getLud16LinkFromLud16(profile!.lud16!)!,
|
||||
amountSats: _amount!,
|
||||
|
Reference in New Issue
Block a user