From 6febf8546fafb7ab263a1e88cb9cb37c4888bc97 Mon Sep 17 00:00:00 2001 From: Roei Erez Date: Tue, 31 May 2022 10:00:59 +0300 Subject: [PATCH] Refactor handling LNURL-Auth - 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 --- lib/bloc/marketplace/vendor_model.dart | 3 + .../initial_walkthrough.dart | 1 - lib/routes/marketplace/lnurl_auth.dart | 45 ++++--- lib/routes/marketplace/lnurl_webview.dart | 19 +-- lib/routes/marketplace/vendor_row.dart | 126 +++++++++--------- lib/routes/marketplace/vendor_webview.dart | 11 +- src/json/vendors.json | 6 +- 7 files changed, 101 insertions(+), 110 deletions(-) diff --git a/lib/bloc/marketplace/vendor_model.dart b/lib/bloc/marketplace/vendor_model.dart index 480516d57..1f24c30f8 100644 --- a/lib/bloc/marketplace/vendor_model.dart +++ b/lib/bloc/marketplace/vendor_model.dart @@ -16,6 +16,7 @@ class VendorModel { final bool onlyShowLogo; final String endpointURI; final String responseID; + final bool webLN; const VendorModel( this.id, @@ -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'; @@ -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, ); } diff --git a/lib/routes/initial_walkthrough/initial_walkthrough.dart b/lib/routes/initial_walkthrough/initial_walkthrough.dart index 28c45737c..7e428b4fa 100644 --- a/lib/routes/initial_walkthrough/initial_walkthrough.dart +++ b/lib/routes/initial_walkthrough/initial_walkthrough.dart @@ -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'; diff --git a/lib/routes/marketplace/lnurl_auth.dart b/lib/routes/marketplace/lnurl_auth.dart index b488f2f9a..2e9362cd3 100644 --- a/lib/routes/marketplace/lnurl_auth.dart +++ b/lib/routes/marketplace/lnurl_auth.dart @@ -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 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 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; - } \ No newline at end of file +Future handleLNUrlAuth(BuildContext context, {VendorModel vendor}) async { + final LNUrlBloc lnurlBloc = AppBlocsProvider.of(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 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; +} diff --git a/lib/routes/marketplace/lnurl_webview.dart b/lib/routes/marketplace/lnurl_webview.dart index c8e9c3257..2821e9fdd 100644 --- a/lib/routes/marketplace/lnurl_webview.dart +++ b/lib/routes/marketplace/lnurl_webview.dart @@ -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'; @@ -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 createState() { @@ -38,8 +25,7 @@ class LNURLWebViewPageState extends State { 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); } @@ -81,7 +67,6 @@ class LNURLWebViewPageState extends State { } return VendorWebViewPage( - widget.accountBloc, "${widget.vendorModel.url}?token=$jwtToken", widget.vendorModel.displayName, ); diff --git a/lib/routes/marketplace/vendor_row.dart b/lib/routes/marketplace/vendor_row.dart index 3e13b05bd..e8d329623 100644 --- a/lib/routes/marketplace/vendor_row.dart +++ b/lib/routes/marketplace/vendor_row.dart @@ -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'; @@ -21,7 +21,6 @@ class VendorRow extends StatelessWidget { @override Widget build(BuildContext context) { - final lnurlBloc = AppBlocsProvider.of(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; @@ -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(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; } diff --git a/lib/routes/marketplace/vendor_webview.dart b/lib/routes/marketplace/vendor_webview.dart index 71b868d37..f60dd19eb 100644 --- a/lib/routes/marketplace/vendor_webview.dart +++ b/lib/routes/marketplace/vendor_webview.dart @@ -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 createState() { @@ -34,6 +29,7 @@ class VendorWebViewPage extends StatefulWidget { class VendorWebViewPageState extends State { WebLNHandlers _weblnHandlers; + AccountBloc _accountBloc; InvoiceBloc _invoiceBloc; bool _isInit = false; NostrEventHandler _nostrEventHandler; @@ -44,8 +40,9 @@ class VendorWebViewPageState extends State { void didChangeDependencies() { super.didChangeDependencies(); if (!_isInit) { + _accountBloc = AppBlocsProvider.of(context); _invoiceBloc = AppBlocsProvider.of(context); - _weblnHandlers = WebLNHandlers(context, widget.accountBloc, _invoiceBloc); + _weblnHandlers = WebLNHandlers(context, _accountBloc, _invoiceBloc); _webViewController = setWebViewController( url: widget._url, onPageFinished: _onPageFinished, diff --git a/src/json/vendors.json b/src/json/vendors.json index a868d8060..1b29b4df9 100644 --- a/src/json/vendors.json +++ b/src/json/vendors.json @@ -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/" @@ -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"