mirror of
https://github.com/nostrlabs-io/zap-stream-flutter.git
synced 2025-06-16 03:58:09 +00:00
@ -138,16 +138,19 @@ class _StreamPage extends State<StreamPage> {
|
|||||||
context: context,
|
context: context,
|
||||||
constraints: BoxConstraints.expand(),
|
constraints: BoxConstraints.expand(),
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
return ZapWidget(
|
return SingleChildScrollView(
|
||||||
pubkey: stream.info.host,
|
primary: false,
|
||||||
target: stream.event,
|
child: ZapWidget(
|
||||||
zapTags:
|
pubkey: stream.info.host,
|
||||||
// tag goal onto zap request
|
target: stream.event,
|
||||||
stream.info.goal != null
|
zapTags:
|
||||||
? [
|
// tag goal onto zap request
|
||||||
["e", stream.info.goal!],
|
stream.info.goal != null
|
||||||
]
|
? [
|
||||||
: null,
|
["e", stream.info.goal!],
|
||||||
|
]
|
||||||
|
: null,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -7,6 +7,7 @@ class BasicButton extends StatelessWidget {
|
|||||||
final EdgeInsetsGeometry? padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
final EdgeInsetsGeometry? margin;
|
final EdgeInsetsGeometry? margin;
|
||||||
final void Function()? onTap;
|
final void Function()? onTap;
|
||||||
|
final bool? disabled;
|
||||||
|
|
||||||
const BasicButton(
|
const BasicButton(
|
||||||
this.child, {
|
this.child, {
|
||||||
@ -15,6 +16,7 @@ class BasicButton extends StatelessWidget {
|
|||||||
this.padding,
|
this.padding,
|
||||||
this.margin,
|
this.margin,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
|
this.disabled,
|
||||||
});
|
});
|
||||||
|
|
||||||
static text(
|
static text(
|
||||||
@ -24,6 +26,7 @@ class BasicButton extends StatelessWidget {
|
|||||||
EdgeInsetsGeometry? margin,
|
EdgeInsetsGeometry? margin,
|
||||||
void Function()? onTap,
|
void Function()? onTap,
|
||||||
double? fontSize,
|
double? fontSize,
|
||||||
|
bool? disabled,
|
||||||
}) {
|
}) {
|
||||||
return BasicButton(
|
return BasicButton(
|
||||||
Text(
|
Text(
|
||||||
@ -34,6 +37,7 @@ class BasicButton extends StatelessWidget {
|
|||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
disabled: disabled,
|
||||||
decoration: decoration,
|
decoration: decoration,
|
||||||
padding: padding ?? EdgeInsets.symmetric(vertical: 4, horizontal: 12),
|
padding: padding ?? EdgeInsets.symmetric(vertical: 4, horizontal: 12),
|
||||||
margin: margin,
|
margin: margin,
|
||||||
@ -44,16 +48,20 @@ class BasicButton extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final defaultBr = BorderRadius.all(Radius.circular(100));
|
final defaultBr = BorderRadius.all(Radius.circular(100));
|
||||||
|
final inner = Container(
|
||||||
|
padding: padding,
|
||||||
|
margin: margin,
|
||||||
|
decoration:
|
||||||
|
decoration ?? BoxDecoration(color: LAYER_2, borderRadius: defaultBr),
|
||||||
|
child: Center(child: child),
|
||||||
|
);
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onTap,
|
onTap: () {
|
||||||
child: Container(
|
if (!(disabled ?? false) && onTap != null) {
|
||||||
padding: padding,
|
onTap!();
|
||||||
margin: margin,
|
}
|
||||||
decoration:
|
},
|
||||||
decoration ??
|
child: (disabled ?? false) ? Opacity(opacity: 0.3, child: inner) : inner,
|
||||||
BoxDecoration(color: LAYER_2, borderRadius: defaultBr),
|
|
||||||
child: Center(child: child),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:clipboard/clipboard.dart';
|
import 'package:clipboard/clipboard.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:ndk/domain_layer/usecases/lnurl/lnurl.dart';
|
import 'package:ndk/domain_layer/usecases/lnurl/lnurl.dart';
|
||||||
import 'package:ndk/ndk.dart';
|
import 'package:ndk/ndk.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
@ -31,6 +32,9 @@ class ZapWidget extends StatefulWidget {
|
|||||||
|
|
||||||
class _ZapWidget extends State<ZapWidget> {
|
class _ZapWidget extends State<ZapWidget> {
|
||||||
final TextEditingController _comment = TextEditingController();
|
final TextEditingController _comment = TextEditingController();
|
||||||
|
final TextEditingController _customAmount = TextEditingController();
|
||||||
|
final FocusNode _customAmountFocus = FocusNode();
|
||||||
|
bool _loading = false;
|
||||||
String? _error;
|
String? _error;
|
||||||
String? _pr;
|
String? _pr;
|
||||||
int? _amount;
|
int? _amount;
|
||||||
@ -67,8 +71,9 @@ class _ZapWidget extends State<ZapWidget> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (_pr == null) ..._inputs(),
|
if (_pr == null && !_loading) ..._inputs(),
|
||||||
if (_pr != null) ..._invoice(),
|
if (_pr != null) ..._invoice(),
|
||||||
|
if (_loading) CircularProgressIndicator(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -83,32 +88,78 @@ class _ZapWidget extends State<ZapWidget> {
|
|||||||
crossAxisCount: 5,
|
crossAxisCount: 5,
|
||||||
mainAxisSpacing: 5,
|
mainAxisSpacing: 5,
|
||||||
crossAxisSpacing: 5,
|
crossAxisSpacing: 5,
|
||||||
childAspectRatio: 1.5,
|
childAspectRatio: 1.9,
|
||||||
),
|
),
|
||||||
itemBuilder: (ctx, idx) => _zapAmount(_zapAmounts[idx]),
|
itemBuilder: (ctx, idx) => _zapAmount(_zapAmounts[idx]),
|
||||||
),
|
),
|
||||||
|
Row(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _customAmount,
|
||||||
|
focusNode: _customAmountFocus,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(labelText: "Custom Amount"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
BasicButton.text(
|
||||||
|
"Confirm",
|
||||||
|
onTap: () {
|
||||||
|
final newAmount = int.tryParse(_customAmount.text);
|
||||||
|
if (newAmount != null) {
|
||||||
|
setState(() {
|
||||||
|
_error = null;
|
||||||
|
_amount = newAmount;
|
||||||
|
_customAmountFocus.unfocus();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_error = "Invalid custom amount";
|
||||||
|
_amount = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _comment,
|
controller: _comment,
|
||||||
decoration: InputDecoration(labelText: "Comment"),
|
decoration: InputDecoration(labelText: "Comment"),
|
||||||
),
|
),
|
||||||
BasicButton.text(
|
BasicButton.text(
|
||||||
"Zap",
|
_amount != null ? "Zap ${formatSats(_amount!)} sats" : "Zap",
|
||||||
|
disabled: _amount == null,
|
||||||
decoration: BoxDecoration(color: LAYER_3, borderRadius: DEFAULT_BR),
|
decoration: BoxDecoration(color: LAYER_3, borderRadius: DEFAULT_BR),
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
try {
|
try {
|
||||||
_loadZap();
|
setState(() {
|
||||||
|
_error = null;
|
||||||
|
_loading = true;
|
||||||
|
});
|
||||||
|
await _loadZap();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_error = e.toString();
|
_error = e.toString();
|
||||||
});
|
});
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_loading = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (_error != null) Text(_error!),
|
if (_error != null)
|
||||||
|
Text(
|
||||||
|
_error!,
|
||||||
|
style: TextStyle(color: WARNING, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _invoice() {
|
List<Widget> _invoice() {
|
||||||
|
final prLink = "lightning:${_pr!}";
|
||||||
|
|
||||||
return [
|
return [
|
||||||
QrImageView(
|
QrImageView(
|
||||||
data: _pr!,
|
data: _pr!,
|
||||||
@ -124,21 +175,50 @@ class _ZapWidget extends State<ZapWidget> {
|
|||||||
onTap: () async {
|
onTap: () async {
|
||||||
await FlutterClipboard.copy(_pr!);
|
await FlutterClipboard.copy(_pr!);
|
||||||
},
|
},
|
||||||
child: Text(_pr!, overflow: TextOverflow.ellipsis),
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(borderRadius: DEFAULT_BR, color: LAYER_2),
|
||||||
|
child: Row(
|
||||||
|
spacing: 4,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.copy, size: 16),
|
||||||
|
Expanded(child: Text(_pr!, overflow: TextOverflow.ellipsis)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
BasicButton.text(
|
FutureBuilder(
|
||||||
"Open in Wallet",
|
future: canLaunchUrlString(prLink),
|
||||||
onTap: () async {
|
builder: (context, v) {
|
||||||
try {
|
if (!(v.data ?? false)) return SizedBox();
|
||||||
await launchUrlString("lightning:${_pr!}");
|
return BasicButton.text(
|
||||||
} catch (e) {
|
"Open in Wallet",
|
||||||
setState(() {
|
onTap: () async {
|
||||||
_error = e is String ? e : e.toString();
|
try {
|
||||||
});
|
await launchUrlString(prLink);
|
||||||
}
|
} catch (e) {
|
||||||
|
if (e is PlatformException) {
|
||||||
|
if (e.code == "ACTIVITY_NOT_FOUND") {
|
||||||
|
setState(() {
|
||||||
|
_error = "No lightning wallet installed";
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_error = e is String ? e : e.toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (_error != null) Text(_error!),
|
|
||||||
|
if (_error != null)
|
||||||
|
Text(
|
||||||
|
_error!,
|
||||||
|
style: TextStyle(color: WARNING, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,6 +284,9 @@ class _ZapWidget extends State<ZapWidget> {
|
|||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap:
|
onTap:
|
||||||
() => setState(() {
|
() => setState(() {
|
||||||
|
_error = null;
|
||||||
|
_customAmount.clear();
|
||||||
|
_customAmountFocus.unfocus();
|
||||||
_amount = n;
|
_amount = n;
|
||||||
}),
|
}),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
Reference in New Issue
Block a user