Skip to content

Commit

Permalink
Refactor handling LNURL-Auth
Browse files Browse the repository at this point in the history
- Apply translations to lnurl_webview
- Update deprecated launch method with launchUrl
- Add "webLN" identifier for lnurl vendors
- Parse endpointURI
- Remove unnecessary params & unused imports
- Correct indentation
- Display a loader while handling LNURL-Auth
- Run "dart format -l 110 ."
- Use localization message on lnurl-auth's error dialog title
  • Loading branch information
roeierez authored and erdemyerebasmaz committed Jun 5, 2024
1 parent d265b20 commit 2f5bade
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 110 deletions.
3 changes: 3 additions & 0 deletions lib/bloc/marketplace/vendor_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class VendorModel {
final bool onlyShowLogo;
final String endpointURI;
final String responseID;
final bool webLN;

const VendorModel(
this.id,
Expand All @@ -24,6 +25,7 @@ class VendorModel {
this.onlyShowLogo,
this.endpointURI,
this.responseID,
this.webLN,
});

String get logo => 'src/icon/vendors/${id.toLowerCase()}_logo_lg.png';
Expand All @@ -37,5 +39,6 @@ class VendorModel {
onlyShowLogo: json["onlyShowLogo"] ?? true,
responseID:
json["endpointURI"] != null && json["responseID"] != null ? json["responseID"] : "lnurl_auth",
webLN: json["webLN"] ?? false,
);
}
1 change: 0 additions & 1 deletion lib/routes/initial_walkthrough/initial_walkthrough.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import 'package:breez/bloc/blocs_provider.dart';
import 'package:breez/bloc/pos_catalog/bloc.dart';
import 'package:breez/bloc/user_profile/user_actions.dart';
import 'package:breez/bloc/user_profile/user_profile_bloc.dart';
import 'package:breez/routes/initial_walkthrough/dialogs/beta_warning_dialog.dart';
import 'package:breez/routes/initial_walkthrough/dialogs/restore_dialog.dart';
import 'package:breez/routes/initial_walkthrough/dialogs/select_backup_provider_dialog.dart';
import 'package:breez/routes/initial_walkthrough/loaders/loader_indicator.dart';
Expand Down
45 changes: 25 additions & 20 deletions lib/routes/marketplace/lnurl_auth.dart
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@

import 'dart:convert';

import 'package:breez/bloc/blocs_provider.dart';
import 'package:breez/bloc/lnurl/lnurl_actions.dart';
import 'package:breez/bloc/lnurl/lnurl_bloc.dart';
import 'package:breez/bloc/lnurl/lnurl_model.dart';
import 'package:breez/bloc/marketplace/vendor_model.dart';
import 'package:breez_translations/breez_translations_locales.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

Future<String> handleLNUrlAuth(VendorModel vendor, LNUrlBloc lnurlBloc, String responsdID) async {
Uri uri = Uri.parse(vendor.url);
var response = await http.get(uri);
if (response.statusCode != 200 && response.statusCode != 201) {
throw Exception("Failed to call ${vendor.displayName} API");
}
Map<String, dynamic> decoded = json.decode(response.body);
String lnUrl = decoded[responsdID] as String;
Fetch fetchAction = Fetch(lnUrl);
lnurlBloc.actionsSink.add(fetchAction);
var fetchResponse = await fetchAction.future;
if (fetchResponse.runtimeType != AuthFetchResponse) {
throw "Invalid URL";
}
AuthFetchResponse authResponse = fetchResponse as AuthFetchResponse;
var action = Login(authResponse, jwt: true);
lnurlBloc.actionsSink.add(action);
return await action.future;
}
Future<String> handleLNUrlAuth(BuildContext context, {VendorModel vendor}) async {
final LNUrlBloc lnurlBloc = AppBlocsProvider.of<LNUrlBloc>(context);
final texts = context.texts();
Uri endpointURI = Uri.parse(vendor.endpointURI);
var response = vendor.id == "lnmarkets" ? await http.post(endpointURI) : await http.get(endpointURI);

if (response.statusCode != 200 && response.statusCode != 201) {
throw Exception(texts.lnurl_webview_error_message(vendor.displayName));
}
Map<String, dynamic> decoded = json.decode(response.body);
String lnUrl = decoded[vendor.responseID] as String;
Fetch fetchAction = Fetch(lnUrl);
lnurlBloc.actionsSink.add(fetchAction);
var fetchResponse = await fetchAction.future;
if (fetchResponse.runtimeType != AuthFetchResponse) {
throw texts.lnurl_webview_error_invalid_url;
}
AuthFetchResponse authResponse = fetchResponse as AuthFetchResponse;
var action = Login(authResponse, jwt: true);
lnurlBloc.actionsSink.add(action);
return await action.future;
}
19 changes: 2 additions & 17 deletions lib/routes/marketplace/lnurl_webview.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'package:breez/bloc/account/account_bloc.dart';
import 'package:breez/bloc/lnurl/lnurl_bloc.dart';
import 'package:breez/bloc/marketplace/vendor_model.dart';
import 'package:breez/routes/marketplace/vendor_webview.dart';
import 'package:breez/widgets/error_dialog.dart';
Expand All @@ -10,20 +8,9 @@ import 'package:flutter/material.dart';
import 'lnurl_auth.dart';

class LNURLWebViewPage extends StatefulWidget {
final AccountBloc accountBloc;
final VendorModel vendorModel;
final LNUrlBloc lnurlBloc;
final Uri endpointURI;
final String responseID;

const LNURLWebViewPage({
Key key,
this.accountBloc,
this.vendorModel,
this.lnurlBloc,
this.endpointURI,
this.responseID,
}) : super(key: key);
const LNURLWebViewPage({Key key, this.vendorModel}) : super(key: key);

@override
State<StatefulWidget> createState() {
Expand All @@ -38,8 +25,7 @@ class LNURLWebViewPageState extends State<LNURLWebViewPage> {
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
handleLNUrlAuth(widget.vendorModel, widget.lnurlBloc, widget.responseID)
.then((jwt) {
handleLNUrlAuth(context, vendor: widget.vendorModel).then((jwt) {
if (mounted) {
setState(() => jwtToken = jwt);
}
Expand Down Expand Up @@ -81,7 +67,6 @@ class LNURLWebViewPageState extends State<LNURLWebViewPage> {
}

return VendorWebViewPage(
widget.accountBloc,
"${widget.vendorModel.url}?token=$jwtToken",
widget.vendorModel.displayName,
);
Expand Down
126 changes: 63 additions & 63 deletions lib/routes/marketplace/vendor_row.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import 'package:breez/bloc/account/account_bloc.dart';
import 'package:breez/bloc/blocs_provider.dart';
import 'package:breez/bloc/lnurl/lnurl_bloc.dart';
import 'package:breez/bloc/marketplace/vendor_model.dart';
import 'package:breez/routes/marketplace/lnurl_auth.dart';
import 'package:breez/theme_data.dart' as theme;
import 'package:breez/widgets/error_dialog.dart';
import 'package:breez/widgets/loader.dart';
import 'package:breez/widgets/route.dart';
import 'package:breez_translations/breez_translations_locales.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
Expand All @@ -21,7 +21,6 @@ class VendorRow extends StatelessWidget {

@override
Widget build(BuildContext context) {
final lnurlBloc = AppBlocsProvider.of<LNUrlBloc>(context);
Color vendorFgColor = theme.vendorTheme[_vendor.id.toLowerCase()]?.iconFgColor ?? Colors.transparent;
Color vendorBgColor = theme.vendorTheme[_vendor.id.toLowerCase()]?.iconBgColor ?? Colors.white;
Color vendorTextColor = theme.vendorTheme[_vendor.id.toLowerCase()]?.textColor ?? Colors.black;
Expand All @@ -47,70 +46,71 @@ class VendorRow extends StatelessWidget {
: Container();

final vendorCard = GestureDetector(
onTap: () async {

// iOS only
if (defaultTargetPlatform == TargetPlatform.iOS) {
try {
var url = _vendor.url;
if (_vendor.id == "lnmarkets" || _vendor.id == "Kollider") {
var responseID =
_vendor.id == "lnmarkets" ? "lnurl" : "lnurl_auth";
var jwtToken = await handleLNUrlAuth(_vendor, lnurlBloc, responseID);
url = url + "?token=$jwtToken";
}
launch(_vendor.url);
}
catch(err) {
promptError(
context,
"Error",
Text(err.toString())
);
onTap: () async {
// iOS only
if (defaultTargetPlatform == TargetPlatform.iOS) {
final navigator = Navigator.of(context);
var loaderRoute = createLoaderRoute(context);
try {
navigator.push(loaderRoute);
var url = _vendor.url;
if (_vendor.webLN) {
var jwtToken = await handleLNUrlAuth(context, vendor: _vendor);
url = "$url?token=$jwtToken";
}
launchUrl(Uri.parse(url));
} catch (err) {
final texts = context.texts();

return promptError(
context,
texts.lnurl_webview_error_title,
Text(err.toString()),
okFunc: () => Navigator.of(context).pop(),
);
} finally {
if (loaderRoute.isActive) {
navigator.removeRoute(loaderRoute);
}
return;
}
return;
}

// non iOS
Navigator.push(context, FadeInRoute(
builder: (_) {
if (_vendor.endpointURI != null) {
var lnurlBloc = AppBlocsProvider.of<LNUrlBloc>(context);
return LNURLWebViewPage(
accountBloc: accountBloc,
vendorModel: _vendor,
lnurlBloc: lnurlBloc,
endpointURI: Uri.tryParse(_vendor.endpointURI),
responseID: _vendor.responseID,
);
}
return VendorWebViewPage(accountBloc, _vendor.url, _vendor.displayName);
},
));
},
child: Container(
margin: const EdgeInsets.fromLTRB(32.0, 8.0, 32.0, 8.0),
constraints: const BoxConstraints.expand(),
decoration: BoxDecoration(
color: vendorBgColor,
boxShadow: [
BoxShadow(
color: theme.BreezColors.grey[600],
blurRadius: 8.0,
)
],
border: Border.all(
color:
vendorBgColor == Colors.white ? Theme.of(context).highlightColor : Colors.transparent,
style: BorderStyle.solid,
width: 1.0),
borderRadius: BorderRadius.circular(14.0)),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: _buildLogo(vendorLogo, vendorTextColor),
// non iOS
Navigator.push(
context,
FadeInRoute(
builder: (_) => (_vendor.endpointURI != null)
? LNURLWebViewPage(vendorModel: _vendor)
: VendorWebViewPage(_vendor.url, _vendor.displayName),
),
);
},
child: Container(
margin: const EdgeInsets.fromLTRB(32.0, 8.0, 32.0, 8.0),
constraints: const BoxConstraints.expand(),
decoration: BoxDecoration(
color: vendorBgColor,
boxShadow: [
BoxShadow(
color: theme.BreezColors.grey[600],
blurRadius: 8.0,
)
],
border: Border.all(
color: vendorBgColor == Colors.white ? Theme.of(context).highlightColor : Colors.transparent,
style: BorderStyle.solid,
width: 1.0,
),
));
borderRadius: BorderRadius.circular(14.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: _buildLogo(vendorLogo, vendorTextColor),
),
),
);

return vendorCard;
}
Expand Down
11 changes: 4 additions & 7 deletions lib/routes/marketplace/vendor_webview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,10 @@ import 'package:webview_flutter_android/webview_flutter_android.dart';
import 'webln_handlers.dart';

class VendorWebViewPage extends StatefulWidget {
final AccountBloc accountBloc;
final String _url;
final String _title;

const VendorWebViewPage(
this.accountBloc,
this._url,
this._title,
);
const VendorWebViewPage(this._url, this._title);

@override
State<StatefulWidget> createState() {
Expand All @@ -34,6 +29,7 @@ class VendorWebViewPage extends StatefulWidget {

class VendorWebViewPageState extends State<VendorWebViewPage> {
WebLNHandlers _weblnHandlers;
AccountBloc _accountBloc;
InvoiceBloc _invoiceBloc;
bool _isInit = false;
NostrEventHandler _nostrEventHandler;
Expand All @@ -44,8 +40,9 @@ class VendorWebViewPageState extends State<VendorWebViewPage> {
void didChangeDependencies() {
super.didChangeDependencies();
if (!_isInit) {
_accountBloc = AppBlocsProvider.of<AccountBloc>(context);
_invoiceBloc = AppBlocsProvider.of<InvoiceBloc>(context);
_weblnHandlers = WebLNHandlers(context, widget.accountBloc, _invoiceBloc);
_weblnHandlers = WebLNHandlers(context, _accountBloc, _invoiceBloc);
_webViewController = setWebViewController(
url: widget._url,
onPageFinished: _onPageFinished,
Expand Down
6 changes: 4 additions & 2 deletions src/json/vendors.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"displayName": "LN Markets",
"url": "https://lnmarkets.com/login/breez",
"endpointURI": "https://api.lnmarkets.com/v1/lnurl/auth",
"responseID": "lnurl"
"responseID": "lnurl",
"webLN": true
},
"Wavlake": {
"url": "http://player.wavlake.com/"
Expand All @@ -21,7 +22,8 @@
"displayName": "The Bitcoin Company",
"url": "https://app.thebitcoincompany.com",
"endpointURI": "https://api.thebitcoincompany.com/auth/auth-lnurl?flatResponse=true",
"responseID": "lnurl"
"responseID": "lnurl",
"webLN": true
},
"Azteco": {
"url": "https://azte.co/bitcoin-vouchers/breez"
Expand Down

0 comments on commit 2f5bade

Please sign in to comment.